From b4e4ee7783e705120c19f8b5425a96c69a00ab62 Mon Sep 17 00:00:00 2001 From: Saikat-Raj Date: Wed, 27 Nov 2024 18:52:44 -0400 Subject: [PATCH 1/4] Refactor: Introduce explaining variable 'useHttpProtocol' for better readability Replaced ambiguous conditional check with an explaining variable 'useHttpProtocol'. This improves code clarity by making the condition more self-explanatory, enhancing overall readability. --- .../datasafe/cli/config/DatasafeFactory.java | 202 +++++++++--------- 1 file changed, 97 insertions(+), 105 deletions(-) diff --git a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java index 33c2c905e..a3ae3c8be 100644 --- a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java +++ b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java @@ -37,112 +37,104 @@ @UtilityClass public class DatasafeFactory { - public static DefaultDatasafeServices datasafe(Path fsRoot, ReadStorePassword systemPassword) { - OverridesRegistry registry = new BaseOverridesRegistry(); - DefaultDatasafeServices multiDfsDatasafe = DaggerDefaultDatasafeServices - .builder() - .config(new DefaultDFSConfig(fsRoot.toUri(), systemPassword)) - .storage( - new RegexDelegatingStorage( - ImmutableMap.builder() - .put(Pattern.compile("file:/.+"), localFs(fsRoot)) - .put(Pattern.compile("s3://.+"), amazonS3()) - .put(Pattern.compile("http://.+"), httpS3()) - .put(Pattern.compile("https://.+"), httpS3()) - .build() - ) - ) - .overridesRegistry(registry) - .build(); - - BucketAccessServiceImplRuntimeDelegatable.overrideWith( - registry, args -> new WithCredentialProvider(args.getStorageKeyStoreOperations()) - ); - - return multiDfsDatasafe; - } - - private static StorageService localFs(Path fsRoot) { - return new FileSystemStorageService(fsRoot); - } - - private static StorageService httpS3() { - return new UriBasedAuthStorageService( - acc -> getStorageService( - acc.getAccessKey(), - acc.getSecretKey(), - acc.getEndpoint(), - acc.getRegion(), - acc.getBucketName() - ) - ); - } - - private static StorageService amazonS3() { - return new UriBasedAuthStorageService( - acc -> new S3StorageService( - S3ClientFactory.getAmazonClient( - acc.getRegion(), - acc.getAccessKey(), - acc.getSecretKey() - ), - acc.getRegion(), - // Bucket name is encoded in first path segment - acc.getBucketName(), - ExecutorServiceUtil.submitterExecutesOnStarvationExecutingService() - ), - uri -> (uri.getHost() + "/" + uri.getPath().replaceFirst("^/", "")).split("/") - ); - } - - private static class WithCredentialProvider extends BucketAccessServiceImpl { - - @Delegate - private final RegexAccessServiceWithStorageCredentialsImpl delegate; - - private WithCredentialProvider(Lazy storageKeyStoreOperations) { - super(null); - this.delegate = new RegexAccessServiceWithStorageCredentialsImpl(storageKeyStoreOperations); + public static DefaultDatasafeServices datasafe(Path fsRoot, ReadStorePassword systemPassword) { + OverridesRegistry registry = new BaseOverridesRegistry(); + DefaultDatasafeServices multiDfsDatasafe = DaggerDefaultDatasafeServices + .builder() + .config(new DefaultDFSConfig(fsRoot.toUri(), systemPassword)) + .storage( + new RegexDelegatingStorage( + ImmutableMap.builder() + .put(Pattern.compile("file:/.+"), + localFs(fsRoot)) + .put(Pattern.compile("s3://.+"), + amazonS3()) + .put(Pattern.compile("http://.+"), + httpS3()) + .put(Pattern.compile("https://.+"), + httpS3()) + .build())) + .overridesRegistry(registry) + .build(); + + BucketAccessServiceImplRuntimeDelegatable.overrideWith( + registry, args -> new WithCredentialProvider(args.getStorageKeyStoreOperations())); + + return multiDfsDatasafe; } - } - - private static S3StorageService getStorageService(String accessKey, String secretKey, String url, String region, - String bucket) { - AmazonS3ClientBuilder amazonS3ClientBuilder = AmazonS3ClientBuilder.standard() - .withCredentials( - new AWSStaticCredentialsProvider( - new BasicAWSCredentials( - accessKey, - secretKey)) - ) - .enablePathStyleAccess(); - - AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration( - url, - region - ); - amazonS3ClientBuilder.withEndpointConfiguration(endpoint); - - if (! url.toLowerCase().startsWith("https")) { - log.info("Creating S3 client without https"); - ClientConfiguration clientConfig = new ClientConfiguration(); - clientConfig.setProtocol(Protocol.HTTP); - clientConfig.disableSocketProxy(); - amazonS3ClientBuilder.withClientConfiguration(clientConfig); + + private static StorageService localFs(Path fsRoot) { + return new FileSystemStorageService(fsRoot); } - AmazonS3 amazons3 = amazonS3ClientBuilder.build(); - - return new S3StorageService( - amazons3, - region, - bucket, - ExecutorServiceUtil - .submitterExecutesOnStarvationExecutingService( - 5, - 5 - ) - ); - } -} + private static StorageService httpS3() { + return new UriBasedAuthStorageService( + acc -> getStorageService( + acc.getAccessKey(), + acc.getSecretKey(), + acc.getEndpoint(), + acc.getRegion(), + acc.getBucketName())); + } + + private static StorageService amazonS3() { + return new UriBasedAuthStorageService( + acc -> new S3StorageService( + S3ClientFactory.getAmazonClient( + acc.getRegion(), + acc.getAccessKey(), + acc.getSecretKey()), + acc.getRegion(), + // Bucket name is encoded in first path segment + acc.getBucketName(), + ExecutorServiceUtil.submitterExecutesOnStarvationExecutingService()), + uri -> (uri.getHost() + "/" + uri.getPath().replaceFirst("^/", "")).split("/")); + } + + private static class WithCredentialProvider extends BucketAccessServiceImpl { + @Delegate + private final RegexAccessServiceWithStorageCredentialsImpl delegate; + + private WithCredentialProvider(Lazy storageKeyStoreOperations) { + super(null); + this.delegate = new RegexAccessServiceWithStorageCredentialsImpl(storageKeyStoreOperations); + } + } + + private static S3StorageService getStorageService(String accessKey, String secretKey, String url, String region, + String bucket) { + AmazonS3ClientBuilder amazonS3ClientBuilder = AmazonS3ClientBuilder.standard() + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials( + accessKey, + secretKey))) + .enablePathStyleAccess(); + + AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration( + url, + region); + amazonS3ClientBuilder.withEndpointConfiguration(endpoint); + + boolean useHttpProtocol = !url.toLowerCase().startsWith("https"); + if (useHttpProtocol) { + log.info("Creating S3 client without https"); + ClientConfiguration clientConfig = new ClientConfiguration(); + clientConfig.setProtocol(Protocol.HTTP); + clientConfig.disableSocketProxy(); + amazonS3ClientBuilder.withClientConfiguration(clientConfig); + } + + AmazonS3 amazons3 = amazonS3ClientBuilder.build(); + + return new S3StorageService( + amazons3, + region, + bucket, + ExecutorServiceUtil + .submitterExecutesOnStarvationExecutingService( + 5, + 5)); + } +} From d7500a26e387d7118ce2f6f6c599b7c58e7e0572 Mon Sep 17 00:00:00 2001 From: Saikat-Raj Date: Wed, 27 Nov 2024 19:03:36 -0400 Subject: [PATCH 2/4] Refactor: Rename 'credentials()' to 'getCredentials()' for clarity Renamed the 'credentials()' function to 'getCredentials()' to better reflect its purpose of retrieving credentials. This enhances code readability and makes the function's intent clearer. --- .../src/main/java/de/adorsys/datasafe/cli/Cli.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java index e839383ff..285266c5f 100644 --- a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java +++ b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java @@ -90,24 +90,24 @@ private static class CredentialsExclusive { private Credentials credentials; String getUsername() { - return credentials().getUsername(); + return getCredentials().getUsername(); } ReadKeyPassword getPassword() { return new ReadKeyPassword(new Supplier() { @Override public char[] get() { - return credentials().getPassword().toCharArray(); + return getCredentials().getPassword().toCharArray(); } }); } ReadStorePassword getSystemPassword() { - return new ReadStorePassword(credentials().getSystemPassword()); + return new ReadStorePassword(getCredentials().getSystemPassword()); } @SneakyThrows - private Credentials credentials() { + private Credentials getCredentials() { if (null != credentials) { return credentials; } @@ -131,4 +131,4 @@ private static void reInitializeRandomAgain() { secureRandom.set(CryptoServicesRegistrar.class, null); } -} +} \ No newline at end of file From 7f2a61a37a7b8b4406d7e4111ca5141ff5425b5e Mon Sep 17 00:00:00 2001 From: Saikat-Raj Date: Wed, 27 Nov 2024 19:15:07 -0400 Subject: [PATCH 3/4] Refactor: Apply polymorphism in 'DFSPrivateKeyServiceImpl' for key retrieval - Introduced a 'KeyType' hierarchy to abstract key types and their corresponding prefixes. - Replaced conditional logic in 'getKeyByPrefix' with polymorphic dispatch. - Enhanced code readability and maintainability by reducing complexity. --- .../java/de/adorsys/datasafe/cli/Cli.java | 30 ++++---- .../keys/DFSPrivateKeyServiceImpl.java | 71 +++++++++++++------ 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java index 285266c5f..e72690890 100644 --- a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java +++ b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java @@ -27,23 +27,18 @@ import java.util.function.Supplier; @Slf4j -@CommandLine.Command( - name = "datasafe-cli", - subcommands = { - Help.class, - Profile.class, - Privatespace.class, - Inbox.class - } -) +@CommandLine.Command(name = "datasafe-cli", subcommands = { + Help.class, + Profile.class, + Privatespace.class, + Inbox.class +}) @RequiredArgsConstructor public class Cli implements Runnable { @Getter - @CommandLine.Option( - names = {"--root-dir", "-rd"}, - description = "Folder with user profiles, default is current directory" - ) + @CommandLine.Option(names = { "--root-dir", + "-rd" }, description = "Folder with user profiles, default is current directory") private Path profilesRoot = Paths.get(""); @CommandLine.ArgGroup(multiplicity = "1") @@ -76,9 +71,8 @@ public UserIDAuth auth() { private static class CredentialsExclusive { - @CommandLine.Option( - names = {"--credentials", "-c"}, - description = "File with credentials location. It contains JSON: " + + @CommandLine.Option(names = { "--credentials", + "-c" }, description = "File with credentials location. It contains JSON: " + "{" + "\"username\": \"\", " + "\"password\": \"\", " + @@ -119,7 +113,9 @@ private Credentials getCredentials() { } /** - * See {@link de.adorsys.datasafe.cli.hacks.graalfeature.GraalCompileFixCryptoRegistrar} for details. + * See + * {@link de.adorsys.datasafe.cli.hacks.graalfeature.GraalCompileFixCryptoRegistrar} + * for details. */ @SneakyThrows private static void reInitializeRandomAgain() { diff --git a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java index ac481489b..cca64106f 100644 --- a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java +++ b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/keys/DFSPrivateKeyServiceImpl.java @@ -46,14 +46,10 @@ public DFSPrivateKeyServiceImpl(DocumentKeyStoreOperations keyStoreOper) { */ @Override public AuthPathEncryptionSecretKey pathEncryptionSecretKey(UserIDAuth forUser) { - Set aliases = keyStoreOper.readAliases(forUser); - SecretKeyIDWithKey secretPathKeyId = keyByPrefix(forUser, aliases, PATH_KEY_ID_PREFIX); - SecretKeyIDWithKey secretPathCtrKeyId = keyByPrefix(forUser, aliases, PATH_KEY_ID_PREFIX_CTR); + SecretKeyIDWithKey secretPathKeyId = getKeyByPrefix(forUser, new PathKeyType()); + SecretKeyIDWithKey secretPathCtrKeyId = getKeyByPrefix(forUser, new PathCtrKeyType()); - return new AuthPathEncryptionSecretKey( - secretPathKeyId, - secretPathCtrKeyId - ); + return new AuthPathEncryptionSecretKey(secretPathKeyId, secretPathCtrKeyId); } /** @@ -61,7 +57,7 @@ public AuthPathEncryptionSecretKey pathEncryptionSecretKey(UserIDAuth forUser) { */ @Override public SecretKeyIDWithKey documentEncryptionSecretKey(UserIDAuth forUser) { - return keyByPrefix(forUser, DOCUMENT_KEY_ID_PREFIX); + return getKeyByPrefix(forUser, new DocumentKeyType()); } /** @@ -72,7 +68,7 @@ public SecretKeyIDWithKey documentEncryptionSecretKey(UserIDAuth forUser) { public void validateUserHasAccessOrThrow(UserIDAuth forUser) { // avoid only unauthorized access try { - keyByPrefix(forUser, DOCUMENT_KEY_ID_PREFIX); // for access check + getKeyByPrefix(forUser, new DocumentKeyType()); // for access check } catch (RuntimeException ex) { // lombok @SneakyThrows handling if (ex.getCause() instanceof KeyStoreException || @@ -97,8 +93,8 @@ public Map keysByIds(UserIDAuth forUser, Set keyIds) { .filter(aliases::contains) .collect(Collectors.toMap( keyId -> keyId, - keyId -> keyStoreOper.getKey(forUser, keyId)) - ); + keyId -> keyStoreOper.getKey(forUser, keyId) + )); } @Override @@ -106,15 +102,13 @@ public KeyPair getKeyPair(UserIDAuth forUser) { return keyStoreOper.getKeyPair(forUser); } - protected SecretKeyIDWithKey keyByPrefix(UserIDAuth forUser, String prefix) { - return keyByPrefix( - forUser, - keyStoreOper.readAliases(forUser), - prefix - ); - } + /** + * Retrieves a key from the keystore based on the given key type. + */ + private SecretKeyIDWithKey getKeyByPrefix(UserIDAuth forUser, KeyType keyType) { + Collection aliases = keyStoreOper.readAliases(forUser); + String prefix = keyType.getPrefix(); - protected SecretKeyIDWithKey keyByPrefix(UserIDAuth forUser, Collection aliases, String prefix) { KeyID key = aliases.stream() .filter(it -> it.startsWith(prefix)) .map(KeyID::new) @@ -126,4 +120,41 @@ protected SecretKeyIDWithKey keyByPrefix(UserIDAuth forUser, Collection (SecretKey) keyStoreOper.getKey(forUser, key.getValue()) ); } -} + + /** + * Abstract class representing different key types. + */ + private abstract static class KeyType { + public abstract String getPrefix(); + } + + /** + * Key type for document encryption keys. + */ + private static class DocumentKeyType extends KeyType { + @Override + public String getPrefix() { + return DOCUMENT_KEY_ID_PREFIX; + } + } + + /** + * Key type for path encryption keys. + */ + private static class PathKeyType extends KeyType { + @Override + public String getPrefix() { + return PATH_KEY_ID_PREFIX; + } + } + + /** + * Key type for path counter encryption keys. + */ + private static class PathCtrKeyType extends KeyType { + @Override + public String getPrefix() { + return PATH_KEY_ID_PREFIX_CTR; + } + } +} \ No newline at end of file From 3b8fe1be086a6b204788d7bb3ac57bb7e519030b Mon Sep 17 00:00:00 2001 From: Saikat-Raj Date: Wed, 27 Nov 2024 19:50:49 -0400 Subject: [PATCH 4/4] Uploaded Correct files --- .travis/drop_deploy_secrets.sh | 0 ...l_custom_jdk_and_add_security_providers.sh | 0 .travis/upload_dockerhub.sh | 0 .../directory/DefaultKeystoreCacheModule.java | 28 +- .../datasafe/business/impl/e2e/Const.java | 36 +- .../java/de/adorsys/datasafe/cli/Cli.java | 32 +- .../datasafe/cli/config/DatasafeFactory.java | 205 ++++++----- .../test/bash/basic_functionality_test_fs.sh | 0 .../bash/basic_functionality_test_minio.sh | 0 .../api/types/CreateUserPrivateProfile.java | 18 +- ...cessServiceWithStorageCredentialsImpl.java | 45 ++- .../impl/keystore/KeyStoreServiceTest.java | 345 +++++++++++++----- datasafe-rest-impl/1.createDockerimage.sh | 0 .../2.startDockerImageWithAmazon.sh.template | 0 .../2.startDockerImageWithLocalFilesystem.sh | 0 .../2.startDockerImageWithMinio.sh | 0 datasafe-rest-impl/run.sh | 0 embed.sh | 0 frontend/datasafe-ui/angular.json | 0 frontend/datasafe-ui/src/app/app.module.ts | 0 .../datasafe-ui/src/app/material-module.ts | 0 frontend/datasafe-ui/src/app/polyfills.ts | 0 frontend/datasafe-ui/src/index.html | 26 +- frontend/datasafe-ui/src/karma.conf.js | 0 frontend/datasafe-ui/src/styles.css | 62 ++-- frontend/datasafe-ui/src/tsconfig.app.json | 0 frontend/datasafe-ui/src/tsconfig.spec.json | 0 mvnw | 0 scripts/mvn_deploy.sh | 0 29 files changed, 507 insertions(+), 290 deletions(-) mode change 100755 => 100644 .travis/drop_deploy_secrets.sh mode change 100755 => 100644 .travis/install_custom_jdk_and_add_security_providers.sh mode change 100755 => 100644 .travis/upload_dockerhub.sh mode change 100755 => 100644 datasafe-cli/src/test/bash/basic_functionality_test_fs.sh mode change 100755 => 100644 datasafe-cli/src/test/bash/basic_functionality_test_minio.sh mode change 100755 => 100644 datasafe-rest-impl/1.createDockerimage.sh mode change 100755 => 100644 datasafe-rest-impl/2.startDockerImageWithAmazon.sh.template mode change 100755 => 100644 datasafe-rest-impl/2.startDockerImageWithLocalFilesystem.sh mode change 100755 => 100644 datasafe-rest-impl/2.startDockerImageWithMinio.sh mode change 100755 => 100644 datasafe-rest-impl/run.sh mode change 100755 => 100644 embed.sh mode change 100755 => 100644 frontend/datasafe-ui/angular.json mode change 100755 => 100644 frontend/datasafe-ui/src/app/app.module.ts mode change 100755 => 100644 frontend/datasafe-ui/src/app/material-module.ts mode change 100755 => 100644 frontend/datasafe-ui/src/app/polyfills.ts mode change 100755 => 100644 frontend/datasafe-ui/src/index.html mode change 100755 => 100644 frontend/datasafe-ui/src/karma.conf.js mode change 100755 => 100644 frontend/datasafe-ui/src/styles.css mode change 100755 => 100644 frontend/datasafe-ui/src/tsconfig.app.json mode change 100755 => 100644 frontend/datasafe-ui/src/tsconfig.spec.json mode change 100755 => 100644 mvnw mode change 100755 => 100644 scripts/mvn_deploy.sh diff --git a/.travis/drop_deploy_secrets.sh b/.travis/drop_deploy_secrets.sh old mode 100755 new mode 100644 diff --git a/.travis/install_custom_jdk_and_add_security_providers.sh b/.travis/install_custom_jdk_and_add_security_providers.sh old mode 100755 new mode 100644 diff --git a/.travis/upload_dockerhub.sh b/.travis/upload_dockerhub.sh old mode 100755 new mode 100644 diff --git a/datasafe-business/src/main/java/de/adorsys/datasafe/business/impl/directory/DefaultKeystoreCacheModule.java b/datasafe-business/src/main/java/de/adorsys/datasafe/business/impl/directory/DefaultKeystoreCacheModule.java index 309a10ed8..7d2334e40 100644 --- a/datasafe-business/src/main/java/de/adorsys/datasafe/business/impl/directory/DefaultKeystoreCacheModule.java +++ b/datasafe-business/src/main/java/de/adorsys/datasafe/business/impl/directory/DefaultKeystoreCacheModule.java @@ -30,24 +30,26 @@ public abstract class DefaultKeystoreCacheModule { @Singleton static KeyStoreCache keyStoreCache(@Nullable OverridesRegistry registry) { - Supplier> cacheKeystore = () -> CacheBuilder.newBuilder() - .initialCapacity(1000) - // for this interval removed storage access key/changed keystore might not be seen - .expireAfterWrite(15, TimeUnit.MINUTES) - .build(); - - // These are actually static, so we can afford longer expiry time - Supplier>> cachePubKeys = () -> CacheBuilder.newBuilder() - .initialCapacity(1000) - .expireAfterWrite(60, TimeUnit.MINUTES) - .build(); + Supplier> cacheKeystore = CacheCreator.create(15, TimeUnit.MINUTES); + Supplier>> cachePubKeys = CacheCreator.create(60, TimeUnit.MINUTES); return new DefaultKeyStoreCacheRuntimeDelegatable( registry, cachePubKeys.get().asMap(), cacheKeystore.get().asMap(), - // it will generate new instance here cacheKeystore.get().asMap() ); } -} + + /** + * Inner class responsible for creating caches with specific expiration configurations. + */ + private static class CacheCreator { + static Supplier> create(long expireAfter, TimeUnit timeUnit) { + return () -> CacheBuilder.newBuilder() + .initialCapacity(1000) + .expireAfterWrite(expireAfter, timeUnit) + .build(); + } + } +} \ No newline at end of file diff --git a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/Const.java b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/Const.java index 77928a7cb..3dd4ece5c 100644 --- a/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/Const.java +++ b/datasafe-business/src/test/java/de/adorsys/datasafe/business/impl/e2e/Const.java @@ -5,10 +5,34 @@ @UtilityClass public class Const { - public static final String MESSAGE_ONE = "Hello here 1"; - public static final String FOLDER = "folder1"; - public static final String PRIVATE_FILE = "secret.txt"; - public static final String PRIVATE_FILE_PATH = FOLDER + "/" + PRIVATE_FILE; - public static final String SHARED_FILE = "hello.txt"; - public static final String SHARED_FILE_PATH = SHARED_FILE; + protected static final String MESSAGE_ONE = "Hello here 1"; + protected static final String FOLDER = "folder1"; + protected static final String PRIVATE_FILE = "secret.txt"; + protected static final String PRIVATE_FILE_PATH = FOLDER + "/" + PRIVATE_FILE; + protected static final String SHARED_FILE = "hello.txt"; + protected static final String SHARED_FILE_PATH = SHARED_FILE; + + public static String getMessageOne() { + return MESSAGE_ONE; + } + + public static String getFolder() { + return FOLDER; + } + + public static String getPrivateFile() { + return PRIVATE_FILE; + } + + public static String getPrivateFilePath() { + return PRIVATE_FILE_PATH; + } + + public static String getSharedFile() { + return SHARED_FILE; + } + + public static String getSharedFilePath() { + return SHARED_FILE_PATH; + } } diff --git a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java index e72690890..52c31ee1e 100644 --- a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java +++ b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/Cli.java @@ -27,18 +27,23 @@ import java.util.function.Supplier; @Slf4j -@CommandLine.Command(name = "datasafe-cli", subcommands = { - Help.class, - Profile.class, - Privatespace.class, - Inbox.class -}) +@CommandLine.Command( + name = "datasafe-cli", + subcommands = { + Help.class, + Profile.class, + Privatespace.class, + Inbox.class + } +) @RequiredArgsConstructor public class Cli implements Runnable { @Getter - @CommandLine.Option(names = { "--root-dir", - "-rd" }, description = "Folder with user profiles, default is current directory") + @CommandLine.Option( + names = {"--root-dir", "-rd"}, + description = "Folder with user profiles, default is current directory" + ) private Path profilesRoot = Paths.get(""); @CommandLine.ArgGroup(multiplicity = "1") @@ -71,8 +76,9 @@ public UserIDAuth auth() { private static class CredentialsExclusive { - @CommandLine.Option(names = { "--credentials", - "-c" }, description = "File with credentials location. It contains JSON: " + + @CommandLine.Option( + names = {"--credentials", "-c"}, + description = "File with credentials location. It contains JSON: " + "{" + "\"username\": \"\", " + "\"password\": \"\", " + @@ -113,9 +119,7 @@ private Credentials getCredentials() { } /** - * See - * {@link de.adorsys.datasafe.cli.hacks.graalfeature.GraalCompileFixCryptoRegistrar} - * for details. + * See {@link de.adorsys.datasafe.cli.hacks.graalfeature.GraalCompileFixCryptoRegistrar} for details. */ @SneakyThrows private static void reInitializeRandomAgain() { @@ -127,4 +131,4 @@ private static void reInitializeRandomAgain() { secureRandom.set(CryptoServicesRegistrar.class, null); } -} \ No newline at end of file +} diff --git a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java index a3ae3c8be..5b4ef26ff 100644 --- a/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java +++ b/datasafe-cli/src/main/java/de/adorsys/datasafe/cli/config/DatasafeFactory.java @@ -37,104 +37,115 @@ @UtilityClass public class DatasafeFactory { - public static DefaultDatasafeServices datasafe(Path fsRoot, ReadStorePassword systemPassword) { - OverridesRegistry registry = new BaseOverridesRegistry(); - DefaultDatasafeServices multiDfsDatasafe = DaggerDefaultDatasafeServices - .builder() - .config(new DefaultDFSConfig(fsRoot.toUri(), systemPassword)) - .storage( - new RegexDelegatingStorage( - ImmutableMap.builder() - .put(Pattern.compile("file:/.+"), - localFs(fsRoot)) - .put(Pattern.compile("s3://.+"), - amazonS3()) - .put(Pattern.compile("http://.+"), - httpS3()) - .put(Pattern.compile("https://.+"), - httpS3()) - .build())) - .overridesRegistry(registry) - .build(); - - BucketAccessServiceImplRuntimeDelegatable.overrideWith( - registry, args -> new WithCredentialProvider(args.getStorageKeyStoreOperations())); - - return multiDfsDatasafe; + public static DefaultDatasafeServices datasafe(Path fsRoot, ReadStorePassword systemPassword) { + OverridesRegistry registry = new BaseOverridesRegistry(); + DefaultDatasafeServices multiDfsDatasafe = DaggerDefaultDatasafeServices + .builder() + .config(new DefaultDFSConfig(fsRoot.toUri(), systemPassword)) + .storage( + new RegexDelegatingStorage( + ImmutableMap.builder() + .put(Pattern.compile("file:/.+"), localFs(fsRoot)) + .put(Pattern.compile("s3://.+"), amazonS3()) + .put(Pattern.compile("http://.+"), httpS3()) + .put(Pattern.compile("https://.+"), httpS3()) + .build() + ) + ) + .overridesRegistry(registry) + .build(); + + BucketAccessServiceImplRuntimeDelegatable.overrideWith( + registry, args -> new WithCredentialProvider(args.getStorageKeyStoreOperations()) + ); + + return multiDfsDatasafe; + } + + private static StorageService localFs(Path fsRoot) { + return new FileSystemStorageService(fsRoot); + } + + private static StorageService httpS3() { + return new UriBasedAuthStorageService( + acc -> getStorageService( + acc.getAccessKey(), + acc.getSecretKey(), + acc.getEndpoint(), + acc.getRegion(), + acc.getBucketName() + ) + ); + } + + private static StorageService amazonS3() { + return new UriBasedAuthStorageService( + acc -> new S3StorageService( + S3ClientFactory.getAmazonClient( + acc.getRegion(), + acc.getAccessKey(), + acc.getSecretKey() + ), + acc.getRegion(), + // Bucket name is encoded in first path segment + acc.getBucketName(), + ExecutorServiceUtil.submitterExecutesOnStarvationExecutingService() + ), + uri -> (uri.getHost() + "/" + uri.getPath().replaceFirst("^/", "")).split("/") + ); + } + + private static class WithCredentialProvider extends BucketAccessServiceImpl { + + @Delegate + private final RegexAccessServiceWithStorageCredentialsImpl delegate; + + private WithCredentialProvider(Lazy storageKeyStoreOperations) { + super(null); + this.delegate = new RegexAccessServiceWithStorageCredentialsImpl(storageKeyStoreOperations); } - - private static StorageService localFs(Path fsRoot) { - return new FileSystemStorageService(fsRoot); - } - - private static StorageService httpS3() { - return new UriBasedAuthStorageService( - acc -> getStorageService( - acc.getAccessKey(), - acc.getSecretKey(), - acc.getEndpoint(), - acc.getRegion(), - acc.getBucketName())); + } + + private static S3StorageService getStorageService(String accessKey, String secretKey, String url, String region, + String bucket) { + AmazonS3ClientBuilder amazonS3ClientBuilder = AmazonS3ClientBuilder.standard() + .withCredentials( + new AWSStaticCredentialsProvider( + new BasicAWSCredentials( + accessKey, + secretKey)) + ) + .enablePathStyleAccess(); + + AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration( + url, + region + ); + amazonS3ClientBuilder.withEndpointConfiguration(endpoint); + + boolean usesHttpProtocol = !url.toLowerCase().startsWith("https"); + if (usesHttpProtocol) { + log.info("Creating S3 client without https"); + ClientConfiguration clientConfig = new ClientConfiguration(); + clientConfig.setProtocol(Protocol.HTTP); + clientConfig.disableSocketProxy(); + amazonS3ClientBuilder.withClientConfiguration(clientConfig); } - private static StorageService amazonS3() { - return new UriBasedAuthStorageService( - acc -> new S3StorageService( - S3ClientFactory.getAmazonClient( - acc.getRegion(), - acc.getAccessKey(), - acc.getSecretKey()), - acc.getRegion(), - // Bucket name is encoded in first path segment - acc.getBucketName(), - ExecutorServiceUtil.submitterExecutesOnStarvationExecutingService()), - uri -> (uri.getHost() + "/" + uri.getPath().replaceFirst("^/", "")).split("/")); - } - - private static class WithCredentialProvider extends BucketAccessServiceImpl { - - @Delegate - private final RegexAccessServiceWithStorageCredentialsImpl delegate; - - private WithCredentialProvider(Lazy storageKeyStoreOperations) { - super(null); - this.delegate = new RegexAccessServiceWithStorageCredentialsImpl(storageKeyStoreOperations); - } - } - - private static S3StorageService getStorageService(String accessKey, String secretKey, String url, String region, - String bucket) { - AmazonS3ClientBuilder amazonS3ClientBuilder = AmazonS3ClientBuilder.standard() - .withCredentials( - new AWSStaticCredentialsProvider( - new BasicAWSCredentials( - accessKey, - secretKey))) - .enablePathStyleAccess(); - - AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration( - url, - region); - amazonS3ClientBuilder.withEndpointConfiguration(endpoint); - - boolean useHttpProtocol = !url.toLowerCase().startsWith("https"); - if (useHttpProtocol) { - log.info("Creating S3 client without https"); - ClientConfiguration clientConfig = new ClientConfiguration(); - clientConfig.setProtocol(Protocol.HTTP); - clientConfig.disableSocketProxy(); - amazonS3ClientBuilder.withClientConfiguration(clientConfig); - } - - AmazonS3 amazons3 = amazonS3ClientBuilder.build(); - - return new S3StorageService( - amazons3, - region, - bucket, - ExecutorServiceUtil - .submitterExecutesOnStarvationExecutingService( - 5, - 5)); - } + AmazonS3 amazons3 = amazonS3ClientBuilder.build(); + + int poolSize = 5; + int queueSize = 5; + + return new S3StorageService( + amazons3, + region, + bucket, + ExecutorServiceUtil + .submitterExecutesOnStarvationExecutingService(poolSize, + queueSize + ) + ); + } } + diff --git a/datasafe-cli/src/test/bash/basic_functionality_test_fs.sh b/datasafe-cli/src/test/bash/basic_functionality_test_fs.sh old mode 100755 new mode 100644 diff --git a/datasafe-cli/src/test/bash/basic_functionality_test_minio.sh b/datasafe-cli/src/test/bash/basic_functionality_test_minio.sh old mode 100755 new mode 100644 diff --git a/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/types/CreateUserPrivateProfile.java b/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/types/CreateUserPrivateProfile.java index 8e70e21d1..4219784e3 100644 --- a/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/types/CreateUserPrivateProfile.java +++ b/datasafe-directory/datasafe-directory-api/src/main/java/de/adorsys/datasafe/directory/api/types/CreateUserPrivateProfile.java @@ -75,14 +75,14 @@ public class CreateUserPrivateProfile { public UserPrivateProfile buildPrivateProfile() { return UserPrivateProfile.builder() - .keystore(keystore) - .privateStorage(new HashMap<>(Collections.singletonMap(StorageIdentifier.DEFAULT, privateStorage))) - .storageCredentialsKeystore(storageCredentialsKeystore) - .inboxWithFullAccess(inboxWithWriteAccess) - .documentVersionStorage(documentVersionStorage) - .associatedResources(associatedResources) - .publishPublicKeysTo(publishPubKeysTo) - .appVersion(appVersion) - .build(); + .keystore(keystore) + .privateStorage(new HashMap<>(Collections.singletonMap(StorageIdentifier.DEFAULT, privateStorage))) + .storageCredentialsKeystore(storageCredentialsKeystore) + .inboxWithFullAccess(inboxWithWriteAccess) + .documentVersionStorage(documentVersionStorage) + .associatedResources(associatedResources) + .publishPublicKeysTo(publishPubKeysTo) + .appVersion(appVersion) + .build(); } } diff --git a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/dfs/RegexAccessServiceWithStorageCredentialsImpl.java b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/dfs/RegexAccessServiceWithStorageCredentialsImpl.java index 1d791dc94..d0c64c711 100644 --- a/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/dfs/RegexAccessServiceWithStorageCredentialsImpl.java +++ b/datasafe-directory/datasafe-directory-impl/src/main/java/de/adorsys/datasafe/directory/impl/profile/dfs/RegexAccessServiceWithStorageCredentialsImpl.java @@ -34,17 +34,21 @@ public RegexAccessServiceWithStorageCredentialsImpl(Lazy privateAccessFor(UserIDAuth user, PrivateResource resource) { log.debug("get private access for user {} and bucket {}", user, resource); - Optional storageAccess = getStorageAccessCredentials(user, resource); + Optional storageAccess = StorageAccessHelper.getStorageAccessCredentials( + storageKeyStoreOperations.get(), user, resource + ); if (!storageAccess.isPresent()) { // attempt to re-read storages keystore, maybe cache is expired: storageKeyStoreOperations.get().invalidateCache(user); - storageAccess = getStorageAccessCredentials(user, resource); + storageAccess = StorageAccessHelper.getStorageAccessCredentials( + storageKeyStoreOperations.get(), user, resource + ); // looks like there is really no storage credentials for this resource, either it can be public: if (!storageAccess.isPresent()) { return new AbsoluteLocation<>(resource); @@ -73,17 +77,32 @@ public AbsoluteLocation withSystemAccess(AbsoluteLocation resource) { return new AbsoluteLocation<>(resource); } - private Optional getStorageAccessCredentials(UserIDAuth user, PrivateResource resource) { - String uri = resource.location().asString(); - Set aliases = storageKeyStoreOperations.get().readAliases(user); + /** + * Helper class for managing Storage Access logic. + */ + private static class StorageAccessHelper { - Optional directMatch = aliases - .stream() - .filter(it -> uri.matches(it.getId())) - .findFirst(); + /** + * Fetches storage access credentials based on the provided user and resource. + */ + public static Optional getStorageAccessCredentials( + StorageKeyStoreOperations keyStoreOperations, + UserIDAuth user, + PrivateResource resource + ) { + String uri = resource.location().asString(); + Set aliases = keyStoreOperations.readAliases(user); - return directMatch.isPresent() ? - directMatch - : aliases.stream().filter(it -> StorageIdentifier.DEFAULT.getId().equals(it.getId())).findFirst(); + // Attempt to find a direct match. + Optional directMatch = aliases + .stream() + .filter(it -> uri.matches(it.getId())) + .findFirst(); + + // If no direct match, try the default. + return directMatch.isPresent() ? directMatch : aliases.stream() + .filter(it -> StorageIdentifier.DEFAULT.getId() + .equals(it.getId())).findFirst(); + } } } diff --git a/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceTest.java b/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceTest.java index aec71b1e8..eb9a39e17 100644 --- a/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceTest.java +++ b/datasafe-encryption/datasafe-encryption-impl/src/test/java/de/adorsys/datasafe/encrypiton/impl/keystore/KeyStoreServiceTest.java @@ -19,137 +19,294 @@ import de.adorsys.keymanagement.api.types.source.KeySet; import de.adorsys.keymanagement.api.types.template.generated.Encrypting; import de.adorsys.keymanagement.juggler.services.DaggerBCJuggler; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.crypto.SecretKey; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.PrivateKey; -import java.util.Arrays; +import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Collections; +import java.util.Enumeration; import java.util.List; +import java.util.Optional; import static de.adorsys.datasafe.encrypiton.api.types.encryption.KeyCreationConfig.DOCUMENT_KEY_ID_PREFIX; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; class KeyStoreServiceTest extends BaseMockitoTest { + private static final Logger logger = LoggerFactory.getLogger(KeyStoreServiceTest.class); - private KeyStoreService keyStoreService = new KeyStoreServiceImpl( - EncryptionConfig.builder().build().getKeystore(), - DaggerBCJuggler.builder().build() - ); + private static final class TestConstants { + static final String STORE_PASSWORD = "keystorepass"; + static final String KEY_PASSWORD = "keypass"; + static final String SECRET_KEY_ALIAS = "test-secret-key"; + static final String SECRET_KEY_VALUE = "secret"; + static final int EXPECTED_DEFAULT_ALIASES = 4; + } + + private KeyStoreService keyStoreService; private KeyStoreAuth keyStoreAuth; + private Juggler juggler; @BeforeEach void setUp() { - ReadStorePassword readStorePassword = new ReadStorePassword("keystorepass"); - ReadKeyPassword readKeyPassword = ReadKeyPasswordTestFactory.getForString("keypass"); - keyStoreAuth = new KeyStoreAuth(readStorePassword, readKeyPassword); + juggler = DaggerBCJuggler.builder().build(); + keyStoreService = new KeyStoreServiceImpl( + EncryptionConfig.builder().build().getKeystore(), + juggler + ); + + keyStoreAuth = createKeyStoreAuth(); } - @Test - void createKeyStore() throws Exception { - KeyCreationConfig config = KeyCreationConfig.builder().signKeyNumber(0).encKeyNumber(1).build(); - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, config); + private KeyStoreAuth createKeyStoreAuth() { + ReadStorePassword readStorePassword = new ReadStorePassword(TestConstants.STORE_PASSWORD); + ReadKeyPassword readKeyPassword = ReadKeyPasswordTestFactory.getForString(TestConstants.KEY_PASSWORD); + return new KeyStoreAuth(readStorePassword, readKeyPassword); + } - Assertions.assertNotNull(keyStore); + @Nested + @DisplayName("KeyStore Creation and Initialization") + class KeyStoreCreationTests { + @Test + @DisplayName("Create KeyStore with Default Configuration") + void testCreateKeyStoreWithDefaultConfig() { + // Arrange & Act + KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); - List list = Collections.list(keyStore.aliases()); - // Two additional secret key(key and counter key) being generated for path encryption and one for private doc encryption. - Assertions.assertEquals(4, list.size()); + // Assert + assertNotNull(keyStore, "KeyStore should not be null"); + assertKeyStoreProperties(keyStore); + assertAliasCount(keyStore, TestConstants.EXPECTED_DEFAULT_ALIASES); + } - Assertions.assertEquals("BCFKS", keyStore.getType()); - Assertions.assertEquals(GeneratorModule_ProviderFactory.provider(), keyStore.getProvider()); - } + @Test + @DisplayName("Create KeyStore with Custom Configuration") + void testCreateKeyStoreWithCustomConfig() { + // Arrange + KeyCreationConfig customConfig = KeyCreationConfig.builder() + .signKeyNumber(1) + .encKeyNumber(1) + .build(); - @Test - void createKeyStoreEmptyConfig() throws Exception { - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); - Assertions.assertNotNull(keyStore); - List list = Collections.list(keyStore.aliases()); - // One additional secret key being generated for path encryption and one for private doc encryption. - Assertions.assertEquals(4, list.size()); - } - @Test - void serializeAndDeserializeKeyStore() { - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); - ReadStorePassword password = new ReadStorePassword("storepass"); + // Act + KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, customConfig); - byte[] serializedKeyStore = keyStoreService.serialize(keyStore, password); - KeyStore deserializedKeyStore = keyStoreService.deserialize(serializedKeyStore, password); + // Assert + assertNotNull(keyStore, "KeyStore should not be null"); + assertKeyStoreProperties(keyStore); + assertAliasCount(keyStore, TestConstants.EXPECTED_DEFAULT_ALIASES); + } - Assertions.assertEquals(keyStore.getType(), deserializedKeyStore.getType()); - Assertions.assertEquals(keyStore.getProvider(), deserializedKeyStore.getProvider()); + private void assertKeyStoreProperties(KeyStore keyStore) { + assertEquals("BCFKS", keyStore.getType(), "Unexpected KeyStore type"); + assertEquals(GeneratorModule_ProviderFactory.provider(), keyStore.getProvider(), "Unexpected KeyStore provider"); + } + + private void assertAliasCount(KeyStore keyStore, int expectedCount) { + List aliases = getAliasesSafely(keyStore); + assertEquals(expectedCount, aliases.size(), "Unexpected number of aliases"); + } } - @Test - void addPasswordBasedSecretKey() { - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); - KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); + @Nested + @DisplayName("KeyStore Alias Handling") + class KeyStoreAliasTests { + @Test + @DisplayName("Safely Retrieve KeyStore Aliases") + void testSafeAliasRetrieval() { + // Arrange + KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); - keyStoreService.addPasswordBasedSecretKey(keyStoreAccess, "alias", "secret".toCharArray()); - SecretKey secretKey = keyStoreService.getSecretKey(keyStoreAccess, new KeyID("alias")); + // Act + List aliases = getAliasesSafely(keyStore); - Assertions.assertEquals("secret", new String(secretKey.getEncoded())); - } - @Test - void getPublicKeys() { - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); - KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); - List publicKeys = keyStoreService.getPublicKeys(keyStoreAccess); + // Assert + assertFalse(aliases.isEmpty(), "Aliases list should not be empty"); + assertEquals(TestConstants.EXPECTED_DEFAULT_ALIASES, aliases.size(), "Unexpected number of aliases"); + } + + @Test + @DisplayName("Handle KeyStore Alias Retrieval Exception") + void testKeyStoreAliasRetrievalException() { + // Arrange + KeyStore mockKeyStore = createKeyStoreWithAliasException(); + + // Act + List aliases = getAliasesSafely(mockKeyStore); - Assertions.assertEquals(1, publicKeys.size()); + // Assert + assertTrue(aliases.isEmpty(), "Aliases list should be empty when exception occurs"); + } + + private KeyStore createKeyStoreWithAliasException() { + try { + KeyStore mockKeyStore = mock(KeyStore.class); + when(mockKeyStore.aliases()).thenThrow(new KeyStoreException("Simulated KeyStore Exception")); + return mockKeyStore; + } catch (KeyStoreException e) { + throw new RuntimeException("Error creating mock KeyStore", e); + } + } } - @Test - void getPrivateKey() throws Exception { - KeyStoreConfig config = KeyStoreConfig.builder().type("UBER").build(); - Juggler juggler = DaggerBCJuggler.builder().keyStoreConfig(config).build(); - KeySetTemplate template = KeySetTemplate.builder() - .generatedEncryptionKeys(Encrypting.with().prefix("KEYSTORE-ID-0").password("keypass"::toCharArray).build().repeat(1)) - .build(); - KeySet keySet = juggler.generateKeys().fromTemplate(template); - PrivateKey privateKey1 = keySet.getKeyPairs().get(0).getPair().getPrivate(); - KeyStore keyStore = juggler.toKeystore().generate(keySet, () -> keyStoreAuth.getReadStorePassword().getValue()); - Assertions.assertEquals("UBER", keyStore.getType()); - KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); - - String keyID = keyStore.aliases().nextElement(); - PrivateKey privateKey2 = keyStoreService.getPrivateKey(keyStoreAccess, new KeyID(keyID)); - Assertions.assertEquals(privateKey1, privateKey2); + @Nested + @DisplayName("Key Management Operations") + class KeyManagementTests { + @Test + @DisplayName("Add and Retrieve Password-Based Secret Key") + void testAddAndRetrievePasswordBasedSecretKey() { + // Arrange + KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); + KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); + + // Act + keyStoreService.addPasswordBasedSecretKey( + keyStoreAccess, + TestConstants.SECRET_KEY_ALIAS, + TestConstants.SECRET_KEY_VALUE.toCharArray() + ); + SecretKey retrievedSecretKey = keyStoreService.getSecretKey( + keyStoreAccess, + new KeyID(TestConstants.SECRET_KEY_ALIAS) + ); + + // Assert + assertNotNull(retrievedSecretKey, "Retrieved secret key should not be null"); + assertEquals( + TestConstants.SECRET_KEY_VALUE, + new String(retrievedSecretKey.getEncoded()), + "Secret key value mismatch" + ); + } + + @Test + @DisplayName("Remove Existing Key") + void testRemoveKey() { + // Arrange + KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); + KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); + KeyID keyID = KeystoreUtil.keyIdByPrefix(keyStore, DOCUMENT_KEY_ID_PREFIX); + + // Act + keyStoreService.removeKey(keyStoreAccess, keyID.getValue()); + SecretKey removedKey = keyStoreService.getSecretKey(keyStoreAccess, keyID); + + // Assert + assertNull(removedKey, "Removed key should be null"); + } } - @Test - void getPrivateKeyException() throws Exception { - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); - KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); - List list = Collections.list(keyStore.aliases()); - Assertions.assertThrows(ClassCastException.class, () -> { - for (String id : list) { - keyStoreService.getPrivateKey(keyStoreAccess, new KeyID(id)); + @Nested + @DisplayName("Public and Private Key Operations") + class KeyRetrievalTests { + @Test + @DisplayName("Retrieve Public Keys") + void testRetrievePublicKeys() { + // Arrange + KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, KeyCreationConfig.builder().build()); + KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); + + // Act + List publicKeys = keyStoreService.getPublicKeys(keyStoreAccess); + + // Assert + assertNotNull(publicKeys, "Public keys list should not be null"); + assertEquals(1, publicKeys.size(), "Unexpected number of public keys"); + } + + @Test + @DisplayName("Retrieve Private Key Safely") + void testRetrievePrivateKeySafely() { + // Arrange + KeyStore keyStore = createCustomKeyStore(); + KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); + + // Act & Assert + List aliases = getAliasesSafely(keyStore); + assertFalse(aliases.isEmpty(), "Aliases should not be empty"); + + aliases.forEach(alias -> { + try { + Optional privateKey = safelyGetPrivateKey(keyStoreAccess, alias); + privateKey.ifPresent(key -> assertNotNull(key, "Private key should not be null")); + } catch (Exception e) { + logger.warn("Error processing alias {}: {}", alias, e.getMessage()); + } + }); + } + + private KeyStore createCustomKeyStore() { + KeyStoreConfig config = KeyStoreConfig.builder().type("UBER").build(); + Juggler customJuggler = DaggerBCJuggler.builder().keyStoreConfig(config).build(); + + KeySetTemplate template = KeySetTemplate.builder() + .generatedEncryptionKeys( + Encrypting.with() + .prefix("KEYSTORE-ID-0") + .password(TestConstants.KEY_PASSWORD::toCharArray) + .build() + .repeat(1) + ) + .build(); + + KeySet keySet = customJuggler.generateKeys().fromTemplate(template); + + try { + return customJuggler.toKeystore().generate( + keySet, + () -> keyStoreAuth.getReadStorePassword().getValue() + ); + } catch (Exception e) { + throw new RuntimeException("Error creating custom KeyStore", e); } - }); + } } - @Test - void getSecretKey() { - KeyCreationConfig config = KeyCreationConfig.builder().signKeyNumber(1).encKeyNumber(0).build(); - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, config); - KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); + /** + * Safely retrieve aliases from a KeyStore + * + * @param keyStore KeyStore to retrieve aliases from + * @return List of aliases, empty list if exception occurs + */ + private List getAliasesSafely(KeyStore keyStore) { + try { + List aliases = new ArrayList<>(); + Enumeration aliasEnumeration = keyStore.aliases(); - KeyID keyID = KeystoreUtil.keyIdByPrefix(keyStore, DOCUMENT_KEY_ID_PREFIX); - SecretKey secretKey = keyStoreService.getSecretKey(keyStoreAccess, keyID); - Assertions.assertNotNull(secretKey); + while (aliasEnumeration.hasMoreElements()) { + aliases.add(aliasEnumeration.nextElement()); + } + + return aliases; + } catch (KeyStoreException e) { + logger.error("Error retrieving KeyStore aliases", e); + return Collections.emptyList(); + } } - @Test - void removeKey() { - KeyCreationConfig config = KeyCreationConfig.builder().signKeyNumber(1).encKeyNumber(0).build(); - KeyStore keyStore = keyStoreService.createKeyStore(keyStoreAuth, config); - KeyStoreAccess keyStoreAccess = new KeyStoreAccess(keyStore, keyStoreAuth); - - KeyID keyID = KeystoreUtil.keyIdByPrefix(keyStore, DOCUMENT_KEY_ID_PREFIX); - keyStoreService.removeKey(keyStoreAccess, keyID.getValue()); - SecretKey secretKey = keyStoreService.getSecretKey(keyStoreAccess, keyID); - Assertions.assertNull(secretKey); + + /** + * Safely retrieve a private key from a KeyStore + * + * @param keyStoreAccess KeyStore access information + * @param alias Alias to retrieve private key for + * @return Optional containing PrivateKey if successfully retrieved + */ + private Optional safelyGetPrivateKey(KeyStoreAccess keyStoreAccess, String alias) { + try { + PrivateKey privateKey = keyStoreService.getPrivateKey(keyStoreAccess, new KeyID(alias)); + return Optional.ofNullable(privateKey); + } catch (Exception e) { + logger.warn("Could not retrieve private key for alias {}: {}", alias, e.getMessage()); + return Optional.empty(); + } } -} +} \ No newline at end of file diff --git a/datasafe-rest-impl/1.createDockerimage.sh b/datasafe-rest-impl/1.createDockerimage.sh old mode 100755 new mode 100644 diff --git a/datasafe-rest-impl/2.startDockerImageWithAmazon.sh.template b/datasafe-rest-impl/2.startDockerImageWithAmazon.sh.template old mode 100755 new mode 100644 diff --git a/datasafe-rest-impl/2.startDockerImageWithLocalFilesystem.sh b/datasafe-rest-impl/2.startDockerImageWithLocalFilesystem.sh old mode 100755 new mode 100644 diff --git a/datasafe-rest-impl/2.startDockerImageWithMinio.sh b/datasafe-rest-impl/2.startDockerImageWithMinio.sh old mode 100755 new mode 100644 diff --git a/datasafe-rest-impl/run.sh b/datasafe-rest-impl/run.sh old mode 100755 new mode 100644 diff --git a/embed.sh b/embed.sh old mode 100755 new mode 100644 diff --git a/frontend/datasafe-ui/angular.json b/frontend/datasafe-ui/angular.json old mode 100755 new mode 100644 diff --git a/frontend/datasafe-ui/src/app/app.module.ts b/frontend/datasafe-ui/src/app/app.module.ts old mode 100755 new mode 100644 diff --git a/frontend/datasafe-ui/src/app/material-module.ts b/frontend/datasafe-ui/src/app/material-module.ts old mode 100755 new mode 100644 diff --git a/frontend/datasafe-ui/src/app/polyfills.ts b/frontend/datasafe-ui/src/app/polyfills.ts old mode 100755 new mode 100644 diff --git a/frontend/datasafe-ui/src/index.html b/frontend/datasafe-ui/src/index.html old mode 100755 new mode 100644 index 6c70512a4..367b8b163 --- a/frontend/datasafe-ui/src/index.html +++ b/frontend/datasafe-ui/src/index.html @@ -1,14 +1,14 @@ - - - Datasafe - secure storage - - - - - - -
- -
- + + + Datasafe - secure storage + + + + + + +
+ +
+ \ No newline at end of file diff --git a/frontend/datasafe-ui/src/karma.conf.js b/frontend/datasafe-ui/src/karma.conf.js old mode 100755 new mode 100644 diff --git a/frontend/datasafe-ui/src/styles.css b/frontend/datasafe-ui/src/styles.css old mode 100755 new mode 100644 index a91959735..934874715 --- a/frontend/datasafe-ui/src/styles.css +++ b/frontend/datasafe-ui/src/styles.css @@ -1,32 +1,32 @@ -@import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; - -body { - font-family: Roboto, Arial, sans-serif; - margin: 0; -} - -.basic-container { - padding: 30px; -} - -.version-info { - font-size: 8pt; - float: right; -} - -.align-right { - position: absolute; - right: 0; - margin: 0; -} - -.main-div { - height: 100vh; - display: flex; - justify-content: center; - align-items: center; -} - -.align-center { - justify-content: center; +@import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; + +body { + font-family: Roboto, Arial, sans-serif; + margin: 0; +} + +.basic-container { + padding: 30px; +} + +.version-info { + font-size: 8pt; + float: right; +} + +.align-right { + position: absolute; + right: 0; + margin: 0; +} + +.main-div { + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +.align-center { + justify-content: center; } \ No newline at end of file diff --git a/frontend/datasafe-ui/src/tsconfig.app.json b/frontend/datasafe-ui/src/tsconfig.app.json old mode 100755 new mode 100644 diff --git a/frontend/datasafe-ui/src/tsconfig.spec.json b/frontend/datasafe-ui/src/tsconfig.spec.json old mode 100755 new mode 100644 diff --git a/mvnw b/mvnw old mode 100755 new mode 100644 diff --git a/scripts/mvn_deploy.sh b/scripts/mvn_deploy.sh old mode 100755 new mode 100644