diff options
Diffstat (limited to 'security')
11 files changed, 370 insertions, 74 deletions
diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java index 35fc7bbe..c2feb17e 100644 --- a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Password.java @@ -26,6 +26,7 @@ import io.vavr.control.Try; import java.security.GeneralSecurityException; import java.util.Arrays; import org.jetbrains.annotations.NotNull; +import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.PasswordEvictedException; /** * Simple password representation. @@ -50,9 +51,10 @@ public class Password { * * @param user of the password */ - public <T> Try<T> use(Function1<char[], Try<T>> user) { - if (value == null) - return Try.failure(new GeneralSecurityException("Password had been already used so it is in cleared state")); + public <T> T use(Function1<char[], T> user) { + if (value == null) { + throw new PasswordEvictedException("Password had been already used so it is in cleared state"); + } try { return user.apply(value); @@ -69,4 +71,14 @@ public class Password { Arrays.fill(value, (char) 0); value = null; } + + /** + * For security reasons this will return a constant value. + * + * @return some predefined string not containing the actual password + */ + @Override + public String toString() { + return "<password>"; + } } diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java index 39828086..38dae0df 100644 --- a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/Passwords.java @@ -20,8 +20,12 @@ package org.onap.dcaegen2.services.sdk.security.ssl; -import io.vavr.control.Try; +import static io.vavr.Function0.constant; + import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; @@ -30,6 +34,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import org.jetbrains.annotations.NotNull; +import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.ReadingPasswordFromFileException; /** * Utility functions for loading passwords. @@ -42,22 +47,97 @@ public final class Passwords { private Passwords() { } - public static @NotNull Try<Password> fromFile(File file) { - return fromPath(file.toPath()); + /** + * Creates password from given char array. + * + * Will directly used the provided array, ie. it will be cleared after password was used. + * + * @param passwd array containing password + * @return Password instance wrapping the provided array + */ + public static @NotNull Password wrap(char[] passwd) { + return new Password(passwd); + } + + /** + * Creates password from given CharSequence. + * + * <em>WARNING</em>: Avoid using this method. It will be impossible to clear memory containing the password. + * + * + * @param passwd the password as CharSequence + * @return Password instance + */ + public static @NotNull Password fromString(CharSequence passwd) { + return constant(passwd) + .andThen(CharBuffer::wrap) + .andThen(Passwords::convertToCharArray) + .andThen(Passwords::wrap) + .apply(); + } + + /** + * Reads password from file. + * + * @param file to read + * @return Password instance with contents of the file + * @throws ReadingPasswordFromFileException when file could not be read + */ + public static @NotNull Password fromFile(File file) { + return constant(file) + .andThen(File::toPath) + .andThen(Passwords::fromPath) + .apply(); + } + + /** + * Reads password from file. + * + * @param path of the file to read + * @return Password instance with contents of the file + * @throws ReadingPasswordFromFileException when file could not be read + */ + public static @NotNull Password fromPath(Path path) { + try { + return constant(Files.readAllBytes(path)) + .andThen(Passwords::decodeChars) + .andThen(Passwords::convertToCharArray) + .andThen(Passwords::wrap) + .apply(); + } catch (IOException e) { + throw new ReadingPasswordFromFileException("Could not read password from " + path, e); + } } - public static @NotNull Try<Password> fromPath(Path path) { - return Try.of(() -> { - final byte[] bytes = Files.readAllBytes(path); - final CharBuffer password = decodeChars(bytes); - final char[] result = convertToCharArray(password); - return new Password(result); - }); + /** + * Reads password from resource. + * + * @param resource URL starting with slash + * @return Password instance with contents of the resource + * @throws ReadingPasswordFromFileException when resource could not be read + */ + public static @NotNull Password fromResource(String resource) { + return constant(resource) + .andThen(Passwords::resourceAsUrl) + .andThen(Passwords::asPath) + .andThen(Passwords::fromPath) + .apply(); } - public static @NotNull Try<Password> fromResource(String resource) { - return Try.of(() -> Paths.get(Passwords.class.getResource(resource).toURI())) - .flatMap(Passwords::fromPath); + private static @NotNull URL resourceAsUrl(String resource) { + final URL resourceUrl = Passwords.class.getResource(resource); + if (resourceUrl == null) { + throw new ReadingPasswordFromFileException("Could not find resource " + resource); + } + return resourceUrl; + } + + private static Path asPath(URL resourceUrl) { + try { + return Paths.get(resourceUrl.toURI()); + } catch (URISyntaxException e) { + throw new ReadingPasswordFromFileException("Could not read password", e); + } } private static @NotNull CharBuffer decodeChars(byte[] bytes) { @@ -80,8 +160,10 @@ public final class Passwords { } private static void clearBuffer(CharBuffer password) { - while (password.remaining() > 0) { - password.put((char) 0); + if(!password.isReadOnly()) { + while (password.remaining() > 0) { + password.put((char) 0); + } } } } diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java index 244e33c8..70cb6c02 100644 --- a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeys.java @@ -28,7 +28,6 @@ import org.immutables.value.Value; */ @Value.Immutable public interface SecurityKeys { - SecurityKeysStore keyStore(); Password keyStorePassword(); diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java index 0ebfc451..401055ca 100644 --- a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SecurityKeysStore.java @@ -22,6 +22,7 @@ package org.onap.dcaegen2.services.sdk.security.ssl; import java.nio.file.Path; import org.immutables.value.Value; +import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.SecurityConfigurationException; /** * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> @@ -44,10 +45,11 @@ public interface SecurityKeysStore { * not be possible. * * @return key store type + * @throws org.onap.dcaegen2.services.sdk.security.ssl.exceptions.SecurityConfigurationException when file type is unknown */ @Value.Default default String type() { return KeyStoreTypes.inferTypeFromExtension(path()) - .getOrElseThrow(() -> new IllegalStateException("Could not determine key store type by file name")); + .getOrElseThrow(() -> new SecurityConfigurationException("Could not determine key store type by file name")); } } diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java index 1f3f4cfd..076490a9 100644 --- a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/SslFactory.java @@ -20,32 +20,46 @@ package org.onap.dcaegen2.services.sdk.security.ssl; +import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.vavr.control.Try; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.security.GeneralSecurityException; import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLException; import javax.net.ssl.TrustManagerFactory; +import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.ReadingSecurityKeysStoreException; +import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.SecurityConfigurationException; +/** + * @since 1.1.1 + */ public class SslFactory { + private static final String EXCEPTION_MESSAGE = "Could not create SSL context"; + /** * Creates Netty SSL <em>client</em> context using provided security keys. * * @param keys - Security keys to be used * @return configured SSL context */ - public Try<SslContext> createSecureClientContext(final SecurityKeys keys) { - return Try.success(SslContextBuilder.forClient()) - .flatMap(ctx -> keyManagerFactory(keys).map(ctx::keyManager)) - .flatMap(ctx -> trustManagerFactory(keys).map(ctx::trustManager)) - .mapTry(SslContextBuilder::build); + public SslContext createSecureClientContext(final SecurityKeys keys) { + try { + return SslContextBuilder.forClient() + .keyManager(keyManagerFactory(keys)) + .trustManager(trustManagerFactory(keys)) + .build(); + } catch (SSLException e) { + throw new SecurityConfigurationException(EXCEPTION_MESSAGE, e); + } } /** @@ -54,11 +68,15 @@ public class SslFactory { * @param keys - Security keys to be used * @return configured SSL context */ - public Try<SslContext> createSecureServerContext(final SecurityKeys keys) { - return keyManagerFactory(keys) - .map(SslContextBuilder::forServer) - .flatMap(ctx -> trustManagerFactory(keys).map(ctx::trustManager)) - .mapTry(SslContextBuilder::build); + public SslContext createSecureServerContext(final SecurityKeys keys) { + try { + return SslContextBuilder.forServer(keyManagerFactory(keys)) + .trustManager(trustManagerFactory(keys)) + .clientAuth(ClientAuth.REQUIRE) + .build(); + } catch (SSLException e) { + throw new SecurityConfigurationException(EXCEPTION_MESSAGE, e); + } } /** @@ -68,40 +86,53 @@ public class SslFactory { * @deprecated Do not use in production. Will trust anyone. */ @Deprecated - public Try<SslContext> createInsecureClientContext() { - return Try.success(SslContextBuilder.forClient()) - .map(ctx -> ctx.trustManager(InsecureTrustManagerFactory.INSTANCE)) - .mapTry(SslContextBuilder::build); + public SslContext createInsecureClientContext() { + try { + return SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build(); + } catch (SSLException e) { + throw new SecurityConfigurationException(EXCEPTION_MESSAGE, e); + } } - private Try<TrustManagerFactory> trustManagerFactory(SecurityKeys keys) { + private TrustManagerFactory trustManagerFactory(SecurityKeys keys) { return trustManagerFactory(keys.trustStore(), keys.trustStorePassword()); } - private Try<KeyManagerFactory> keyManagerFactory(SecurityKeys keys) { + private KeyManagerFactory keyManagerFactory(SecurityKeys keys) { return keyManagerFactory(keys.keyStore(), keys.keyStorePassword()); } - private Try<KeyManagerFactory> keyManagerFactory(SecurityKeysStore store, Password password) { - return password.useChecked(passwordChars -> { - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(loadKeyStoreFromFile(store, passwordChars), passwordChars); - return kmf; + private KeyManagerFactory keyManagerFactory(SecurityKeysStore store, Password password) { + return password.use(passwordChars -> { + try { + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(loadKeyStoreFromFile(store, passwordChars), passwordChars); + return kmf; + } catch (GeneralSecurityException | IOException ex) { + throw new ReadingSecurityKeysStoreException("Could not read private keys from store", ex); + } }); } - private Try<TrustManagerFactory> trustManagerFactory(SecurityKeysStore store, Password password) { - return password.useChecked(passwordChars -> { - TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(loadKeyStoreFromFile(store, passwordChars)); - return tmf; + private TrustManagerFactory trustManagerFactory(SecurityKeysStore store, Password password) { + return password.use(passwordChars -> { + try { + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(loadKeyStoreFromFile(store, passwordChars)); + return tmf; + } catch (GeneralSecurityException | IOException ex) { + throw new ReadingSecurityKeysStoreException("Could not read trusted keys from store", ex); + } }); } private KeyStore loadKeyStoreFromFile(SecurityKeysStore store, char[] keyStorePassword) - throws GeneralSecurityException, IOException { + throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { KeyStore ks = KeyStore.getInstance(store.type()); ks.load(Files.newInputStream(store.path(), StandardOpenOption.READ), keyStorePassword); return ks; + } } diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/PasswordEvictedException.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/PasswordEvictedException.java new file mode 100644 index 00000000..508fc6e2 --- /dev/null +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/PasswordEvictedException.java @@ -0,0 +1,32 @@ +/* + * ============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.ssl.exceptions; + +/** + * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> + * @since 1.1.2 + */ +public class PasswordEvictedException extends RuntimeException { + + public PasswordEvictedException(String message) { + super(message); + } +} diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/ReadingPasswordFromFileException.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/ReadingPasswordFromFileException.java new file mode 100644 index 00000000..b39e01c9 --- /dev/null +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/ReadingPasswordFromFileException.java @@ -0,0 +1,36 @@ +/* + * ============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.ssl.exceptions; + +/** + * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> + * @since 1.1.2 + */ +public class ReadingPasswordFromFileException extends SecurityConfigurationException { + + public ReadingPasswordFromFileException(String message) { + super(message); + } + + public ReadingPasswordFromFileException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/ReadingSecurityKeysStoreException.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/ReadingSecurityKeysStoreException.java new file mode 100644 index 00000000..f8e0cfd5 --- /dev/null +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/ReadingSecurityKeysStoreException.java @@ -0,0 +1,36 @@ +/* + * ============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.ssl.exceptions; + +/** + * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> + * @since 1.1.2 + */ +public class ReadingSecurityKeysStoreException extends SecurityConfigurationException { + + public ReadingSecurityKeysStoreException(String message) { + super(message); + } + + public ReadingSecurityKeysStoreException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/SecurityConfigurationException.java b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/SecurityConfigurationException.java new file mode 100644 index 00000000..08c38df7 --- /dev/null +++ b/security/ssl/src/main/java/org/onap/dcaegen2/services/sdk/security/ssl/exceptions/SecurityConfigurationException.java @@ -0,0 +1,36 @@ +/* + * ============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.ssl.exceptions; + +/** + * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> + * @since 1.1.2 + */ +public class SecurityConfigurationException extends RuntimeException { + + public SecurityConfigurationException(String message) { + super(message); + } + + public SecurityConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java index ede227eb..41143f61 100644 --- a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java +++ b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordTest.java @@ -24,10 +24,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import io.vavr.collection.Array; -import io.vavr.control.Try; import java.security.GeneralSecurityException; import java.util.Arrays; import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.PasswordEvictedException; /** * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> @@ -66,10 +66,10 @@ class PasswordTest { final Password cut = new Password("ala ma kota".toCharArray()); // when & then - useThePassword(cut).get(); + useThePassword(cut); - assertThatExceptionOfType(GeneralSecurityException.class).isThrownBy(() -> - useThePassword(cut).get()); + assertThatExceptionOfType(PasswordEvictedException.class).isThrownBy(() -> + useThePassword(cut)); } @Test @@ -80,8 +80,8 @@ class PasswordTest { // when & then cut.clear(); - assertThatExceptionOfType(GeneralSecurityException.class).isThrownBy(() -> - useThePassword(cut).get()); + assertThatExceptionOfType(PasswordEvictedException.class).isThrownBy(() -> + useThePassword(cut)); } @Test @@ -97,8 +97,8 @@ class PasswordTest { assertAllCharsAreNull(passwordChars); } - private Try<Object> useThePassword(Password cut) { - return cut.use((pass) -> Try.success(42)); + private void useThePassword(Password cut) { + cut.use((pass) -> null); } private void assertAllCharsAreNull(char[] passwordChars) { diff --git a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java index 07c5afe8..13d7f213 100644 --- a/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java +++ b/security/ssl/src/test/java/org/onap/dcaegen2/services/sdk/security/ssl/PasswordsTest.java @@ -21,15 +21,17 @@ package org.onap.dcaegen2.services.sdk.security.ssl; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -import io.vavr.control.Try; import java.io.File; import java.net.URISyntaxException; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.UUID; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.onap.dcaegen2.services.sdk.security.ssl.exceptions.ReadingPasswordFromFileException; /** * @author <a href="mailto:piotr.jaszczyk@nokia.com">Piotr Jaszczyk</a> @@ -38,28 +40,62 @@ import org.junit.jupiter.api.Test; class PasswordsTest { @Test + void wrap() { + // given + final char[] passwd = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}; + + // when + final Password result = Passwords.wrap(passwd); + + // then + assertThat(extractPassword(result)).isEqualTo("password"); + assertThat(passwd).containsOnly('\0'); + } + + @Test + void fromString() { + // given + final String passwd = "password"; + + // when + final Password result = Passwords.fromString(passwd); + + // then + assertThat(extractPassword(result)).isEqualTo("password"); + } + + @Test void fromFile() { // given final File file = new File("./src/test/resources/password.txt"); // when - final Try<Password> result = Passwords.fromFile(file); + final Password result = Passwords.fromFile(file); // then - assertSuccessful(result); assertThat(extractPassword(result)).isEqualTo("ja baczewski\n2nd line"); } @Test + void fromFileWhenNotExisting() { + // given + final File file = new File("./not existing file"); + + // when & then + assertThatThrownBy(() -> Passwords.fromFile(file)) + .isInstanceOf(ReadingPasswordFromFileException.class) + .hasCauseInstanceOf(NoSuchFileException.class); + } + + @Test void fromPath() throws URISyntaxException { // given final Path path = Paths.get(PasswordsTest.class.getResource("/password.txt").toURI()); // when - final Try<Password> result = Passwords.fromPath(path); + final Password result = Passwords.fromPath(path); // then - assertSuccessful(result); assertThat(extractPassword(result)).isEqualTo("ja baczewski\n2nd line"); } @@ -69,11 +105,10 @@ class PasswordsTest { final Path path = Paths.get("/", UUID.randomUUID().toString()); // when - final Try<Password> result = Passwords.fromPath(path); + Assertions.assertThrows(ReadingPasswordFromFileException.class, () -> { + Passwords.fromPath(path); + }); - // then - assertThat(result.isFailure()).describedAs("Try.failure?").isTrue(); - assertThat(result.getCause()).isInstanceOf(NoSuchFileException.class); } @Test @@ -82,18 +117,13 @@ class PasswordsTest { final String resource = "/password.txt"; // when - final Try<Password> result = Passwords.fromResource(resource); + final Password result = Passwords.fromResource(resource); // then - assertSuccessful(result); assertThat(extractPassword(result)).isEqualTo("ja baczewski\n2nd line"); } - private void assertSuccessful(Try<Password> result) { - assertThat(result.isSuccess()).describedAs("Try.success?").isTrue(); - } - - private String extractPassword(Try<Password> result) { - return result.flatMap(pass -> pass.useChecked(String::new)).get(); + private String extractPassword(Password pass) { + return pass.use(String::new); } }
\ No newline at end of file |