diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index febccd0c..49d8d05c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -156,27 +156,27 @@ jobs: aws-region: us-west-2 - name: Build the servers - run: cd test-server && make build-all-servers FILTER=ruby,go,cpp,php + run: cd test-server && make build-all-servers FILTER=ruby,go,cpp,php,java env: MAKEFLAGS: -j${{ steps.cpu-count.outputs.count }} AWS_REGION: us-west-2 - name: Start the servers - run: cd test-server && make start-all-servers FILTER=ruby,go,cpp,php + run: cd test-server && make start-all-servers FILTER=ruby,go,cpp,php,java env: AWS_REGION: us-west-2 TEST_SERVER_S3_BUCKET: ${{ vars.TEST_SERVER_S3_BUCKET }} TEST_SERVER_KMS_KEY_ARN: ${{ vars.TEST_SERVER_KMS_KEY_ARN }} - name: Wait for servers to start - run: cd test-server && make wait-all-servers FILTER=ruby,go,cpp,php + run: cd test-server && make wait-all-servers FILTER=ruby,go,cpp,php,java env: AWS_REGION: us-west-2 TEST_SERVER_S3_BUCKET: ${{ vars.TEST_SERVER_S3_BUCKET }} TEST_SERVER_KMS_KEY_ARN: ${{ vars.TEST_SERVER_KMS_KEY_ARN }} - name: Run run-tests - run: cd test-server && make run-tests FILTER=ruby,go,cpp,php + run: cd test-server && make run-tests FILTER=ruby,go,cpp,php,java env: AWS_REGION: us-west-2 TEST_SERVER_S3_BUCKET: ${{ vars.TEST_SERVER_S3_BUCKET }} @@ -194,7 +194,7 @@ jobs: test-server/*/net-v3-server.log - name: Stop the servers - run: cd test-server && make stop-servers FILTER=ruby,go,cpp,php + run: cd test-server && make stop-servers FILTER=ruby,go,cpp,php,java - name: Upload results if: always() diff --git a/.gitmodules b/.gitmodules index 1e395ed8..3adca9cb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,12 +18,11 @@ [submodule "test-server/java-v3-transition-server/s3ec-staging"] path = test-server/java-v3-transition-server/s3ec-staging url = git@github.com:aws/private-amazon-s3-encryption-client-java-staging.git - branch = imabhichow/s3ec-transition + branch = imabhichow/transition-read-kc [submodule "test-server/java-v4-server/s3ec-staging"] path = test-server/java-v4-server/s3ec-staging url = git@github.com:aws/private-amazon-s3-encryption-client-java-staging.git branch = imabhichow/add-kc -; branch = s3ec/improved [submodule "test-server/specification"] path = test-server/specification url = git@github.com:awslabs/private-aws-encryption-sdk-specification-staging.git diff --git a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java index c20fd5a6..b60eeb04 100644 --- a/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java +++ b/test-server/java-tests/src/it/java/software/amazon/encryption/s3/TestUtils.java @@ -134,7 +134,7 @@ public class TestUtils { public static final Set TRANSITION_VERSIONS = Set.of( - // JAVA_V3_TRANSITION, + JAVA_V3_TRANSITION, GO_V3_TRANSITION, // NET_V2_TRANSITION, NET_V3_TRANSITION, @@ -172,8 +172,7 @@ public class TestUtils { servers.put(NET_V4, new LanguageServerTarget(NET_V4, "8090")); servers.put(RUBY_V3, new LanguageServerTarget(RUBY_V3, "8092")); servers.put(PHP_V3, new LanguageServerTarget(PHP_V3, "8093")); - // TODO: Create and add transition servers - // servers.put(JAVA_V3_TRANSITION, new LanguageServerTarget(JAVA_V3_TRANSITION, "8094")); + servers.put(JAVA_V3_TRANSITION, new LanguageServerTarget(JAVA_V3_TRANSITION, "8094")); servers.put(GO_V3_TRANSITION, new LanguageServerTarget(GO_V3_TRANSITION, "8095")); // servers.put(NET_V2_TRANSITION, new LanguageServerTarget(NET_V2_TRANSITION, "8096")); servers.put(RUBY_V2_TRANSITION, new LanguageServerTarget(RUBY_V2_TRANSITION, "8098")); diff --git a/test-server/java-v3-server/.duvet/config.toml b/test-server/java-v3-server/.duvet/config.toml index f03424b2..988fb5fa 100644 --- a/test-server/java-v3-server/.duvet/config.toml +++ b/test-server/java-v3-server/.duvet/config.toml @@ -1,7 +1,7 @@ '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json" [[source]] -pattern = "s3ec-staging/*.java" +pattern = "s3ec-staging/**/*.java" # Include required specifications here [[specification]] diff --git a/test-server/java-v3-transition-server/.duvet/config.toml b/test-server/java-v3-transition-server/.duvet/config.toml index f29cd058..65605eaa 100644 --- a/test-server/java-v3-transition-server/.duvet/config.toml +++ b/test-server/java-v3-transition-server/.duvet/config.toml @@ -1,7 +1,7 @@ '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json" [[source]] -pattern = "s3ec-staging/*.java" +pattern = "s3ec-staging/**/*.java" # Include required specifications here [[specification]] diff --git a/test-server/java-v3-transition-server/build.gradle.kts b/test-server/java-v3-transition-server/build.gradle.kts index 5b3a9234..7f249d65 100644 --- a/test-server/java-v3-transition-server/build.gradle.kts +++ b/test-server/java-v3-transition-server/build.gradle.kts @@ -14,7 +14,7 @@ dependencies { implementation("software.amazon.smithy.java:aws-server-restjson:$smithyJavaVersion") // S3EC from local Maven repository (installed by mvn install) - implementation("software.amazon.encryption.s3:amazon-s3-encryption-client-java:3.4.0-TRANSITION") + implementation("software.amazon.encryption.s3:amazon-s3-encryption-client-java:3.4.0-read-kc") } // Use that application plugin to start the service via the `run` task. diff --git a/test-server/java-v3-transition-server/s3ec-staging b/test-server/java-v3-transition-server/s3ec-staging index c8527040..6413811b 160000 --- a/test-server/java-v3-transition-server/s3ec-staging +++ b/test-server/java-v3-transition-server/s3ec-staging @@ -1 +1 @@ -Subproject commit c852704026ebc1c46bd11b6d5ab6d9a37ec1985d +Subproject commit 6413811bb81037999b8238e02047e0e403f78c1f diff --git a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java index 3ef7ee60..425c0334 100644 --- a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java +++ b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java @@ -1,9 +1,8 @@ package software.amazon.encryption.s3; import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.encryption.s3.algorithms.AlgorithmSuite; import software.amazon.encryption.s3.internal.InstructionFileConfig; -import software.amazon.encryption.s3.S3EncryptionClient; +import software.amazon.encryption.s3.algorithms.AlgorithmSuite; import software.amazon.encryption.s3.materials.AesKeyring; import software.amazon.encryption.s3.materials.Keyring; import software.amazon.encryption.s3.materials.KmsKeyring; @@ -31,148 +30,150 @@ import java.util.UUID; import static software.amazon.encryption.s3.CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT; -import static software.amazon.encryption.s3.model.EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY; +import static software.amazon.encryption.s3.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT; +import static software.amazon.encryption.s3.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT; public class CreateClientOperationImpl implements CreateClientOperation { - private Map clientCache_; - - public CreateClientOperationImpl(Map clientCache) { - clientCache_ = clientCache; - } - - // Copied from S3EC. - private boolean onlyOneNonNull(Object... values) { - boolean haveOneNonNull = false; - for (Object o : values) { - if (o != null) { - if (haveOneNonNull) { - return false; + private final Map clientCache_; + + public CreateClientOperationImpl(Map clientCache) { + clientCache_ = clientCache; + } + + // Copied from S3EC. + private boolean onlyOneNonNull(Object... values) { + boolean haveOneNonNull = false; + for (Object o : values) { + if (o != null) { + if (haveOneNonNull) { + return false; + } + + haveOneNonNull = true; + } } - haveOneNonNull = true; - } + return haveOneNonNull; } - return haveOneNonNull; - } - - @Override - public CreateClientOutput createClient(CreateClientInput input, RequestContext context) { - try { - KeyMaterial key = input.getConfig().getKeyMaterial(); - if (!onlyOneNonNull(key.getAesKey(), key.getKmsKeyId(), key.getRsaKey())) { - throw new RuntimeException("KeyMaterial must be only one, non-null input!"); - } - Keyring keyring; - if (key.getAesKey() != null) { - byte[] keyBytes = new byte[key.getAesKey().remaining()]; - key.getAesKey().get(keyBytes); - keyring = AesKeyring.builder() - .wrappingKey(new SecretKeySpec(keyBytes, "AES")) - .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) - .build(); - } else if (key.getRsaKey() != null) { + @Override + public CreateClientOutput createClient(CreateClientInput input, RequestContext context) { try { - byte[] keyBytes = new byte[key.getRsaKey().remaining()]; - key.getRsaKey().get(keyBytes); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyFactory.generatePrivate(keySpec); - RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec( - privateKey.getModulus(), - privateKey.getPublicExponent() - ); - - // Generate public key - PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); - - keyring = RsaKeyring.builder() - .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) - .wrappingKeyPair(PartialRsaKeyPair.builder() - .publicKey(publicKey) - .privateKey(privateKey).build()) - .build(); - } catch (NoSuchAlgorithmException | InvalidKeySpecException nse) { - throw GenericServerError.builder() - .message(nse.getMessage()) - .build(); + KeyMaterial key = input.getConfig().getKeyMaterial(); + if (!onlyOneNonNull(key.getAesKey(), key.getKmsKeyId(), key.getRsaKey())) { + throw new RuntimeException("KeyMaterial must be only one, non-null input!"); + } + Keyring keyring; + if (key.getAesKey() != null) { + byte[] keyBytes = new byte[key.getAesKey().remaining()]; + key.getAesKey().get(keyBytes); + keyring = AesKeyring.builder() + .wrappingKey(new SecretKeySpec(keyBytes, "AES")) + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .build(); + } else if (key.getRsaKey() != null) { + try { + byte[] keyBytes = new byte[key.getRsaKey().remaining()]; + key.getRsaKey().get(keyBytes); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyFactory.generatePrivate(keySpec); + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec( + privateKey.getModulus(), + privateKey.getPublicExponent() + ); + + // Generate public key + PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); + + keyring = RsaKeyring.builder() + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .wrappingKeyPair(PartialRsaKeyPair.builder() + .publicKey(publicKey) + .privateKey(privateKey).build()) + .build(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException nse) { + throw GenericServerError.builder() + .message(nse.getMessage()) + .build(); + } + } else if (key.getKmsKeyId() != null) { + keyring = KmsKeyring.builder() + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .wrappingKeyId(key.getKmsKeyId()) + .build(); + } else { + throw new RuntimeException("No KeyMaterial found!"); + } + + // V3 Transition server configuration + // Existing Builder defaults to FORBID_ENCRYPT and ALG_AES_256_GCM_IV12_TAG16_NO_KDF + S3EncryptionClient.Builder s3ClientBuilder = S3EncryptionClient.builder() + .keyring(keyring) + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .enableLegacyUnauthenticatedModes(input.getConfig().isEnableLegacyUnauthenticatedModes()); + + // Instruction File Put Configuration + boolean instFilePut = false; + if (input.getConfig().getInstructionFileConfig() != null) { + instFilePut = input.getConfig().getInstructionFileConfig().isEnableInstructionFilePutObject(); + s3ClientBuilder.instructionFileConfig(InstructionFileConfig.builder() + .instructionFileClient(S3Client.create()) + .enableInstructionFilePutObject(instFilePut) + .build()); + } + + // Configure commitment policy if provided + if (input.getConfig().getCommitmentPolicy() != null) { + CommitmentPolicy policy = getCommitmentPolicy(input.getConfig().getCommitmentPolicy()); + s3ClientBuilder.commitmentPolicy(policy); + } + + // Configure encryption algorithm if provided + if (input.getConfig().getEncryptionAlgorithm() != null) { + AlgorithmSuite algorithm = getAlgorithmSuite(input.getConfig().getEncryptionAlgorithm()); + s3ClientBuilder.encryptionAlgorithm(algorithm); + } + + S3Client s3Client = s3ClientBuilder.build(); + + UUID uuid = UUID.randomUUID(); + String uuidString = uuid.toString(); + clientCache_.put(uuidString, s3Client); + return CreateClientOutput.builder() + .clientId(uuidString) + .build(); + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw GenericServerError.builder() + .message(stackTrace) + .build(); } - } else if (key.getKmsKeyId() != null) { - keyring = KmsKeyring.builder() - .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) - .wrappingKeyId(key.getKmsKeyId()) - .build(); - } else { - throw new RuntimeException("No KeyMaterial found!"); - } - - boolean instFilePut = false; - if (input.getConfig().getInstructionFileConfig() != null) { - instFilePut = input.getConfig().getInstructionFileConfig().isEnableInstructionFilePutObject(); - } - - // V3-Transitional server configuration - S3EncryptionClient.Builder clientBuilder = S3EncryptionClient.builder() - .instructionFileConfig(InstructionFileConfig.builder() - .instructionFileClient(S3Client.create()) - .enableInstructionFilePutObject(instFilePut) - .build()) - .keyring(keyring); - - // Configure commitment policy if provided ( feature) - if (input.getConfig().getCommitmentPolicy() != null) { - CommitmentPolicy policy = getCommitmentPolicy(input); - clientBuilder.commitmentPolicy(policy); - } - // V3-Transitional default: No commitment policy (null) for backward compatibility - - // Configure encryption algorithm if provided ( feature) - if (input.getConfig().getEncryptionAlgorithm() != null) { - AlgorithmSuite algorithm = getAlgorithmSuite(input); - clientBuilder.encryptionAlgorithm(algorithm); - } else { - // V3-Transitional default: Legacy algorithm for backward compatibility - clientBuilder.encryptionAlgorithm(AlgorithmSuite.ALG_AES_256_GCM_IV12_TAG16_NO_KDF); - } - - S3Client s3Client = clientBuilder.build(); - UUID uuid = UUID.randomUUID(); - String uuidString = uuid.toString(); - clientCache_.put(uuidString, s3Client); - return CreateClientOutput.builder() - .clientId(uuidString) - .build(); - } catch (Exception e) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw GenericServerError.builder() - .message(stackTrace) - .build(); } - } - - private static AlgorithmSuite getAlgorithmSuite(CreateClientInput input) { - if (input.getConfig().getEncryptionAlgorithm().equals(EncryptionAlgorithm.ALG_AES_256_CBC_IV16_NO_KDF)) { - return AlgorithmSuite.ALG_AES_256_CBC_IV16_NO_KDF; - } else if (input.getConfig().getEncryptionAlgorithm().equals(EncryptionAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF)) { - return AlgorithmSuite.ALG_AES_256_GCM_IV12_TAG16_NO_KDF; - } else if (input.getConfig().getEncryptionAlgorithm().equals(EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY)) { - return AlgorithmSuite.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY; - } else { - throw new RuntimeException("Unknown encryption algorithm: " + input.getConfig().getEncryptionAlgorithm()); + + private static AlgorithmSuite getAlgorithmSuite(EncryptionAlgorithm input) { + if (input.equals(EncryptionAlgorithm.ALG_AES_256_CBC_IV16_NO_KDF)) { + return AlgorithmSuite.ALG_AES_256_CBC_IV16_NO_KDF; + } else if (input.equals(EncryptionAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF)) { + return AlgorithmSuite.ALG_AES_256_GCM_IV12_TAG16_NO_KDF; + } else if (input.equals(EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY)) { + return AlgorithmSuite.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY; + } else { + throw new RuntimeException("Unknown encryption algorithm: " + input); + } } - } - - private static software.amazon.encryption.s3.CommitmentPolicy getCommitmentPolicy(CreateClientInput input) { - if (input.getConfig().getCommitmentPolicy().equals(software.amazon.encryption.s3.model.CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)) { - return FORBID_ENCRYPT_ALLOW_DECRYPT; - } else if (input.getConfig().getCommitmentPolicy().equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT)) { - return null; - } else if (input.getConfig().getCommitmentPolicy().equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)) { - return null; - } else { - throw new RuntimeException("Unknown commitment policy: " + input.getConfig().getCommitmentPolicy()); + + private static software.amazon.encryption.s3.CommitmentPolicy getCommitmentPolicy(software.amazon.encryption.s3.model.CommitmentPolicy input) { + if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)) { + return FORBID_ENCRYPT_ALLOW_DECRYPT; + } else if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT)) { + return REQUIRE_ENCRYPT_ALLOW_DECRYPT; + } else if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)) { + return REQUIRE_ENCRYPT_REQUIRE_DECRYPT; + } else { + throw new RuntimeException("Unknown commitment policy: " + input); + } } - } } diff --git a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java index e7c5493f..86749489 100644 --- a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java +++ b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java @@ -23,50 +23,52 @@ import static software.amazon.encryption.s3.MetadataUtils.metadataMapToList; public class GetObjectOperationImpl implements GetObjectOperation { - private Map clientCache_; - public GetObjectOperationImpl(Map clientCache) { - clientCache_ = clientCache; - } - @Override - public GetObjectOutput getObject(GetObjectInput input, RequestContext context) { - try { - S3Client s3Client = clientCache_.get(input.getClientID()); - Map ecMap = metadataListToMap(input.getMetadata()); + private Map clientCache_; - try { - ResponseBytes resp = s3Client.getObjectAsBytes(builder -> builder - .bucket(input.getBucket()) - .key(input.getKey()) - .overrideConfiguration(withAdditionalConfiguration(ecMap))); + public GetObjectOperationImpl(Map clientCache) { + clientCache_ = clientCache; + } + + @Override + public GetObjectOutput getObject(GetObjectInput input, RequestContext context) { + try { + S3Client s3Client = clientCache_.get(input.getClientID()); + Map ecMap = metadataListToMap(input.getMetadata()); + + try { + ResponseBytes resp = s3Client.getObjectAsBytes(builder -> builder + .bucket(input.getBucket()) + .key(input.getKey()) + .overrideConfiguration(withAdditionalConfiguration(ecMap))); - List mdAsList = metadataMapToList(resp.response().metadata()); - // Can't use asBB else it gets mad bc cant access backing array - ByteBuffer bb = ByteBuffer.wrap(resp.asByteArray()); - GetObjectOutput output = GetObjectOutput.builder() - .body(bb) - .metadata(mdAsList) - .build(); - return output; - } catch (S3EncryptionClientException s3EncryptionClientException) { - // Modeled exceptions MUST be returned as such - StringWriter sw = new StringWriter(); - s3EncryptionClientException.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw S3EncryptionClientError.builder() - .message(stackTrace) - .build(); - } - } catch (Exception e) { - // Don't wrap modeled errors - if (e instanceof S3EncryptionClientError) { - throw e; - } - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw GenericServerError.builder() - .message(stackTrace) - .build(); + List mdAsList = metadataMapToList(resp.response().metadata()); + // Can't use asBB else it gets mad bc cant access backing array + ByteBuffer bb = ByteBuffer.wrap(resp.asByteArray()); + GetObjectOutput output = GetObjectOutput.builder() + .body(bb) + .metadata(mdAsList) + .build(); + return output; + } catch (S3EncryptionClientException s3EncryptionClientException) { + // Modeled exceptions MUST be returned as such + StringWriter sw = new StringWriter(); + s3EncryptionClientException.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw S3EncryptionClientError.builder() + .message(stackTrace) + .build(); + } + } catch (Exception e) { + // Don't wrap modeled errors + if (e instanceof S3EncryptionClientError) { + throw e; + } + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw GenericServerError.builder() + .message(stackTrace) + .build(); + } } - } } diff --git a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java index 036289ec..9eba6a3d 100644 --- a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java +++ b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java @@ -9,35 +9,35 @@ public class MetadataUtils { - /** - * Annoyingly, Smithy doesn't provide an interface for map types - * in HTTP headers, so we have to do the serde ourselves - */ - public static List metadataMapToList(Map md) { - List mdAsList = new ArrayList<>(md.size()); - for (Map.Entry keyValue : md.entrySet()) { - mdAsList.add("[" + keyValue.getKey() + "]:[" + keyValue.getValue() + "]"); + /** + * Annoyingly, Smithy doesn't provide an interface for map types + * in HTTP headers, so we have to do the serde ourselves + */ + public static List metadataMapToList(Map md) { + List mdAsList = new ArrayList<>(md.size()); + for (Map.Entry keyValue : md.entrySet()) { + mdAsList.add("[" + keyValue.getKey() + "]:[" + keyValue.getValue() + "]"); + } + return mdAsList; } - return mdAsList; - } - public static Map metadataListToMap(List mdList) { - Map md = new HashMap<>(); - for (String entry : mdList) { - // Split on "]:[" to separate key and value - String[] parts = entry.split("]:\\["); - if (parts.length == 2) { - // Remove remaining brackets from start and end - String key = parts[0].substring(1); - String value = parts[1].substring(0, parts[1].length() - 1); - md.put(key, value); - } else { - throw GenericServerError.builder() - .message("Malformed metadata list entry: " + entry) - .build(); - } + public static Map metadataListToMap(List mdList) { + Map md = new HashMap<>(); + for (String entry : mdList) { + // Split on "]:[" to separate key and value + String[] parts = entry.split("]:\\["); + if (parts.length == 2) { + // Remove remaining brackets from start and end + String key = parts[0].substring(1); + String value = parts[1].substring(0, parts[1].length() - 1); + md.put(key, value); + } else { + throw GenericServerError.builder() + .message("Malformed metadata list entry: " + entry) + .build(); + } + } + return md; } - return md; - } } diff --git a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java index 4c772673..ca76e83f 100644 --- a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java +++ b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java @@ -20,36 +20,36 @@ public class PutObjectOperationImpl implements PutObjectOperation { - private Map clientCache_; + private Map clientCache_; - public PutObjectOperationImpl(Map clientCache) { - clientCache_ = clientCache; - } + public PutObjectOperationImpl(Map clientCache) { + clientCache_ = clientCache; + } - @Override - public PutObjectOutput putObject(PutObjectInput input, RequestContext context) { - try { - final Map metadata = metadataListToMap(input.getMetadata()); - S3Client s3Client = clientCache_.get(input.getClientID()); - s3Client.putObject(builder -> builder - .bucket(input.getBucket()) - .key(input.getKey()) - .overrideConfiguration(withAdditionalConfiguration(metadata)), - RequestBody.fromByteBuffer(input.getBody()) - ); - // The real S3 doesn't provide bucket/key/metadata, so Test doesn't need to either, but we do anyway - return PutObjectOutput.builder() - .bucket(input.getBucket()) - .key(input.getKey()) - .metadata(input.getMetadata()) - .build(); - } catch (Exception e) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw GenericServerError.builder() - .message(stackTrace) - .build(); + @Override + public PutObjectOutput putObject(PutObjectInput input, RequestContext context) { + try { + final Map metadata = metadataListToMap(input.getMetadata()); + S3Client s3Client = clientCache_.get(input.getClientID()); + s3Client.putObject(builder -> builder + .bucket(input.getBucket()) + .key(input.getKey()) + .overrideConfiguration(withAdditionalConfiguration(metadata)), + RequestBody.fromByteBuffer(input.getBody()) + ); + // The real S3 doesn't provide bucket/key/metadata, so Test doesn't need to either, but we do anyway + return PutObjectOutput.builder() + .bucket(input.getBucket()) + .key(input.getKey()) + .metadata(input.getMetadata()) + .build(); + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw GenericServerError.builder() + .message(stackTrace) + .build(); + } } - } } diff --git a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java index 32af5fc1..a992cabd 100644 --- a/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java +++ b/test-server/java-v3-transition-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; + import software.amazon.smithy.java.server.Server; import software.amazon.encryption.s3.service.S3ECTestServer; @@ -29,14 +30,14 @@ public void run() { Map clientCache = new ConcurrentHashMap<>(); Server server = Server.builder() - .endpoints(endpoint) - .addService( - S3ECTestServer.builder() - .addCreateClientOperation(new CreateClientOperationImpl(clientCache)) - .addGetObjectOperation(new GetObjectOperationImpl(clientCache)) - .addPutObjectOperation(new PutObjectOperationImpl(clientCache)) - .build()) - .build(); + .endpoints(endpoint) + .addService( + S3ECTestServer.builder() + .addCreateClientOperation(new CreateClientOperationImpl(clientCache)) + .addGetObjectOperation(new GetObjectOperationImpl(clientCache)) + .addPutObjectOperation(new PutObjectOperationImpl(clientCache)) + .build()) + .build(); System.out.println("Starting server..."); server.start(); try { diff --git a/test-server/java-v4-server/.duvet/config.toml b/test-server/java-v4-server/.duvet/config.toml index f29cd058..65605eaa 100644 --- a/test-server/java-v4-server/.duvet/config.toml +++ b/test-server/java-v4-server/.duvet/config.toml @@ -1,7 +1,7 @@ '$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json" [[source]] -pattern = "s3ec-staging/*.java" +pattern = "s3ec-staging/**/*.java" # Include required specifications here [[specification]] diff --git a/test-server/java-v4-server/s3ec-staging b/test-server/java-v4-server/s3ec-staging index c101d149..db0c743e 160000 --- a/test-server/java-v4-server/s3ec-staging +++ b/test-server/java-v4-server/s3ec-staging @@ -1 +1 @@ -Subproject commit c101d14946a8279387b73482a31c03c2f269c9a4 +Subproject commit db0c743eec335d16e6dceaf2b09d84becb0f74f8 diff --git a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java index ab85df62..cb20d5ac 100644 --- a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java +++ b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/CreateClientOperationImpl.java @@ -1,6 +1,5 @@ package software.amazon.encryption.s3; -import software.amazon.awssdk.core.traits.Trait; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.encryption.s3.internal.InstructionFileConfig; import software.amazon.encryption.s3.algorithms.AlgorithmSuite; @@ -35,147 +34,145 @@ import static software.amazon.encryption.s3.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT; public class CreateClientOperationImpl implements CreateClientOperation { - private Map clientCache_; - - public CreateClientOperationImpl(Map clientCache) { - clientCache_ = clientCache; - } - - // Copied from S3EC. - private boolean onlyOneNonNull(Object... values) { - boolean haveOneNonNull = false; - for (Object o : values) { - if (o != null) { - if (haveOneNonNull) { - return false; + private final Map clientCache_; + + public CreateClientOperationImpl(Map clientCache) { + clientCache_ = clientCache; + } + + // Copied from S3EC. + private boolean onlyOneNonNull(Object... values) { + boolean haveOneNonNull = false; + for (Object o : values) { + if (o != null) { + if (haveOneNonNull) { + return false; + } + + haveOneNonNull = true; + } } - haveOneNonNull = true; - } + return haveOneNonNull; } - return haveOneNonNull; - } - - @Override - public CreateClientOutput createClient(CreateClientInput input, RequestContext context) { - try { - KeyMaterial key = input.getConfig().getKeyMaterial(); - if (!onlyOneNonNull(key.getAesKey(), key.getKmsKeyId(), key.getRsaKey())) { - throw new RuntimeException("KeyMaterial must be only one, non-null input!"); - } - Keyring keyring; - if (key.getAesKey() != null) { - byte[] keyBytes = new byte[key.getAesKey().remaining()]; - key.getAesKey().get(keyBytes); - keyring = AesKeyring.builder() - .wrappingKey(new SecretKeySpec(keyBytes, "AES")) - .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) - .build(); - } else if (key.getRsaKey() != null) { + @Override + public CreateClientOutput createClient(CreateClientInput input, RequestContext context) { try { - byte[] keyBytes = new byte[key.getRsaKey().remaining()]; - key.getRsaKey().get(keyBytes); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyFactory.generatePrivate(keySpec); - RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec( - privateKey.getModulus(), - privateKey.getPublicExponent() - ); - - // Generate public key - PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); - - keyring = RsaKeyring.builder() - .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) - .wrappingKeyPair(PartialRsaKeyPair.builder() - .publicKey(publicKey) - .privateKey(privateKey).build()) - .build(); - } catch (NoSuchAlgorithmException | InvalidKeySpecException nse) { - throw GenericServerError.builder() - .message(nse.getMessage()) - .build(); + KeyMaterial key = input.getConfig().getKeyMaterial(); + if (!onlyOneNonNull(key.getAesKey(), key.getKmsKeyId(), key.getRsaKey())) { + throw new RuntimeException("KeyMaterial must be only one, non-null input!"); + } + Keyring keyring; + if (key.getAesKey() != null) { + byte[] keyBytes = new byte[key.getAesKey().remaining()]; + key.getAesKey().get(keyBytes); + keyring = AesKeyring.builder() + .wrappingKey(new SecretKeySpec(keyBytes, "AES")) + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .build(); + } else if (key.getRsaKey() != null) { + try { + byte[] keyBytes = new byte[key.getRsaKey().remaining()]; + key.getRsaKey().get(keyBytes); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyFactory.generatePrivate(keySpec); + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec( + privateKey.getModulus(), + privateKey.getPublicExponent() + ); + + // Generate public key + PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); + + keyring = RsaKeyring.builder() + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .wrappingKeyPair(PartialRsaKeyPair.builder() + .publicKey(publicKey) + .privateKey(privateKey).build()) + .build(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException nse) { + throw GenericServerError.builder() + .message(nse.getMessage()) + .build(); + } + } else if (key.getKmsKeyId() != null) { + keyring = KmsKeyring.builder() + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .wrappingKeyId(key.getKmsKeyId()) + .build(); + } else { + throw new RuntimeException("No KeyMaterial found!"); + } + + // V4-Improved server configuration + S3EncryptionClient.Builder s3ClientBuilder = S3EncryptionClient.builderV4() + .keyring(keyring) + .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) + .enableLegacyUnauthenticatedModes(input.getConfig().isEnableLegacyUnauthenticatedModes()); + + // Client Creation + boolean instFilePut = false; + if (input.getConfig().getInstructionFileConfig() != null) { + instFilePut = input.getConfig().getInstructionFileConfig().isEnableInstructionFilePutObject(); + s3ClientBuilder.instructionFileConfig(InstructionFileConfig.builder() + .instructionFileClient(S3Client.create()) + .enableInstructionFilePutObject(instFilePut) + .build()); + } + + // Configure commitment policy if provided + if (input.getConfig().getCommitmentPolicy() != null) { + CommitmentPolicy policy = getCommitmentPolicy(input.getConfig().getCommitmentPolicy()); + s3ClientBuilder.commitmentPolicy(policy); + } + + // Configure encryption algorithm if provided + if (input.getConfig().getEncryptionAlgorithm() != null) { + AlgorithmSuite algorithm = getAlgorithmSuite(input.getConfig().getEncryptionAlgorithm()); + s3ClientBuilder.encryptionAlgorithm(algorithm); + } + + S3Client s3Client = s3ClientBuilder.build(); + + UUID uuid = UUID.randomUUID(); + String uuidString = uuid.toString(); + clientCache_.put(uuidString, s3Client); + return CreateClientOutput.builder() + .clientId(uuidString) + .build(); + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw GenericServerError.builder() + .message(stackTrace) + .build(); } - } else if (key.getKmsKeyId() != null) { - keyring = KmsKeyring.builder() - .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) - .wrappingKeyId(key.getKmsKeyId()) - .build(); - } else { - throw new RuntimeException("No KeyMaterial found!"); - } - - - // Client Creation - boolean instFilePut = false; - if (input.getConfig().getInstructionFileConfig() != null) { - instFilePut = input.getConfig().getInstructionFileConfig().isEnableInstructionFilePutObject(); - } - - // Configure commitment policy if provided - software.amazon.encryption.s3.CommitmentPolicy policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT; - if (input.getConfig().getCommitmentPolicy() != null) { - policy = getCommitmentPolicy(input.getConfig().getCommitmentPolicy()); - } - - // Configure encryption algorithm if provided - AlgorithmSuite algorithm = AlgorithmSuite.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY; - if (input.getConfig().getEncryptionAlgorithm() != null) { - algorithm = getAlgorithmSuite(input.getConfig().getEncryptionAlgorithm()); - } - - // V4-Improved server configuration - S3EncryptionClient s3Client = S3EncryptionClient.builderV4() - .instructionFileConfig(InstructionFileConfig.builder() - .instructionFileClient(S3Client.create()) - .enableInstructionFilePutObject(instFilePut) - .build()) - .keyring(keyring) - .commitmentPolicy(policy) - .encryptionAlgorithm(algorithm) - .enableLegacyWrappingAlgorithms(input.getConfig().isEnableLegacyWrappingAlgorithms()) - .enableLegacyUnauthenticatedModes(input.getConfig().isEnableLegacyUnauthenticatedModes()) - .build(); - - UUID uuid = UUID.randomUUID(); - String uuidString = uuid.toString(); - clientCache_.put(uuidString, s3Client); - return CreateClientOutput.builder() - .clientId(uuidString) - .build(); - } catch (Exception e) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw GenericServerError.builder() - .message(stackTrace) - .build(); } - } - - private static AlgorithmSuite getAlgorithmSuite(EncryptionAlgorithm input) { - if (input.equals(EncryptionAlgorithm.ALG_AES_256_CBC_IV16_NO_KDF)) { - return AlgorithmSuite.ALG_AES_256_CBC_IV16_NO_KDF; - } else if (input.equals(EncryptionAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF)) { - return AlgorithmSuite.ALG_AES_256_GCM_IV12_TAG16_NO_KDF; - } else if (input.equals(EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY)) { - return AlgorithmSuite.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY; - } else { - throw new RuntimeException("Unknown encryption algorithm: " + input); + + private static AlgorithmSuite getAlgorithmSuite(EncryptionAlgorithm input) { + if (input.equals(EncryptionAlgorithm.ALG_AES_256_CBC_IV16_NO_KDF)) { + return AlgorithmSuite.ALG_AES_256_CBC_IV16_NO_KDF; + } else if (input.equals(EncryptionAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF)) { + return AlgorithmSuite.ALG_AES_256_GCM_IV12_TAG16_NO_KDF; + } else if (input.equals(EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY)) { + return AlgorithmSuite.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY; + } else { + throw new RuntimeException("Unknown encryption algorithm: " + input); + } } - } - - private static software.amazon.encryption.s3.CommitmentPolicy getCommitmentPolicy(software.amazon.encryption.s3.model.CommitmentPolicy input) { - if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)) { - return FORBID_ENCRYPT_ALLOW_DECRYPT; - } else if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT)) { - return REQUIRE_ENCRYPT_ALLOW_DECRYPT; - } else if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)) { - return REQUIRE_ENCRYPT_REQUIRE_DECRYPT; - } else { - throw new RuntimeException("Unknown commitment policy: " + input); + + private static software.amazon.encryption.s3.CommitmentPolicy getCommitmentPolicy(software.amazon.encryption.s3.model.CommitmentPolicy input) { + if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT)) { + return FORBID_ENCRYPT_ALLOW_DECRYPT; + } else if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT)) { + return REQUIRE_ENCRYPT_ALLOW_DECRYPT; + } else if (input.equals(software.amazon.encryption.s3.model.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)) { + return REQUIRE_ENCRYPT_REQUIRE_DECRYPT; + } else { + throw new RuntimeException("Unknown commitment policy: " + input); + } } - } } diff --git a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java index e7c5493f..17e9a8ee 100644 --- a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java +++ b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/GetObjectOperationImpl.java @@ -3,70 +3,70 @@ import software.amazon.awssdk.core.ResponseBytes; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.encryption.s3.S3EncryptionClientException; -import software.amazon.smithy.java.server.RequestContext; import software.amazon.encryption.s3.model.GenericServerError; import software.amazon.encryption.s3.model.GetObjectInput; import software.amazon.encryption.s3.model.GetObjectOutput; import software.amazon.encryption.s3.model.S3EncryptionClientError; import software.amazon.encryption.s3.service.GetObjectOperation; +import software.amazon.smithy.java.server.RequestContext; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import static software.amazon.encryption.s3.S3EncryptionClient.withAdditionalConfiguration; import static software.amazon.encryption.s3.MetadataUtils.metadataListToMap; import static software.amazon.encryption.s3.MetadataUtils.metadataMapToList; +import static software.amazon.encryption.s3.S3EncryptionClient.withAdditionalConfiguration; public class GetObjectOperationImpl implements GetObjectOperation { - private Map clientCache_; - public GetObjectOperationImpl(Map clientCache) { - clientCache_ = clientCache; - } - @Override - public GetObjectOutput getObject(GetObjectInput input, RequestContext context) { - try { - S3Client s3Client = clientCache_.get(input.getClientID()); - Map ecMap = metadataListToMap(input.getMetadata()); + private final Map clientCache_; + + public GetObjectOperationImpl(Map clientCache) { + clientCache_ = clientCache; + } + + @Override + public GetObjectOutput getObject(GetObjectInput input, RequestContext context) { + try { + S3Client s3Client = clientCache_.get(input.getClientID()); + Map ecMap = metadataListToMap(input.getMetadata()); - try { - ResponseBytes resp = s3Client.getObjectAsBytes(builder -> builder - .bucket(input.getBucket()) - .key(input.getKey()) - .overrideConfiguration(withAdditionalConfiguration(ecMap))); + try { + ResponseBytes resp = s3Client.getObjectAsBytes(builder -> builder + .bucket(input.getBucket()) + .key(input.getKey()) + .overrideConfiguration(withAdditionalConfiguration(ecMap))); - List mdAsList = metadataMapToList(resp.response().metadata()); - // Can't use asBB else it gets mad bc cant access backing array - ByteBuffer bb = ByteBuffer.wrap(resp.asByteArray()); - GetObjectOutput output = GetObjectOutput.builder() - .body(bb) - .metadata(mdAsList) - .build(); - return output; - } catch (S3EncryptionClientException s3EncryptionClientException) { - // Modeled exceptions MUST be returned as such - StringWriter sw = new StringWriter(); - s3EncryptionClientException.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw S3EncryptionClientError.builder() - .message(stackTrace) - .build(); - } - } catch (Exception e) { - // Don't wrap modeled errors - if (e instanceof S3EncryptionClientError) { - throw e; - } - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw GenericServerError.builder() - .message(stackTrace) - .build(); + List mdAsList = metadataMapToList(resp.response().metadata()); + // Can't use asBB else it gets mad bc cant access backing array + ByteBuffer bb = ByteBuffer.wrap(resp.asByteArray()); + GetObjectOutput output = GetObjectOutput.builder() + .body(bb) + .metadata(mdAsList) + .build(); + return output; + } catch (S3EncryptionClientException s3EncryptionClientException) { + // Modeled exceptions MUST be returned as such + StringWriter sw = new StringWriter(); + s3EncryptionClientException.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw S3EncryptionClientError.builder() + .message(stackTrace) + .build(); + } + } catch (Exception e) { + // Don't wrap modeled errors + if (e instanceof S3EncryptionClientError) { + throw e; + } + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw GenericServerError.builder() + .message(stackTrace) + .build(); + } } - } } diff --git a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java index 036289ec..9eba6a3d 100644 --- a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java +++ b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/MetadataUtils.java @@ -9,35 +9,35 @@ public class MetadataUtils { - /** - * Annoyingly, Smithy doesn't provide an interface for map types - * in HTTP headers, so we have to do the serde ourselves - */ - public static List metadataMapToList(Map md) { - List mdAsList = new ArrayList<>(md.size()); - for (Map.Entry keyValue : md.entrySet()) { - mdAsList.add("[" + keyValue.getKey() + "]:[" + keyValue.getValue() + "]"); + /** + * Annoyingly, Smithy doesn't provide an interface for map types + * in HTTP headers, so we have to do the serde ourselves + */ + public static List metadataMapToList(Map md) { + List mdAsList = new ArrayList<>(md.size()); + for (Map.Entry keyValue : md.entrySet()) { + mdAsList.add("[" + keyValue.getKey() + "]:[" + keyValue.getValue() + "]"); + } + return mdAsList; } - return mdAsList; - } - public static Map metadataListToMap(List mdList) { - Map md = new HashMap<>(); - for (String entry : mdList) { - // Split on "]:[" to separate key and value - String[] parts = entry.split("]:\\["); - if (parts.length == 2) { - // Remove remaining brackets from start and end - String key = parts[0].substring(1); - String value = parts[1].substring(0, parts[1].length() - 1); - md.put(key, value); - } else { - throw GenericServerError.builder() - .message("Malformed metadata list entry: " + entry) - .build(); - } + public static Map metadataListToMap(List mdList) { + Map md = new HashMap<>(); + for (String entry : mdList) { + // Split on "]:[" to separate key and value + String[] parts = entry.split("]:\\["); + if (parts.length == 2) { + // Remove remaining brackets from start and end + String key = parts[0].substring(1); + String value = parts[1].substring(0, parts[1].length() - 1); + md.put(key, value); + } else { + throw GenericServerError.builder() + .message("Malformed metadata list entry: " + entry) + .build(); + } + } + return md; } - return md; - } } diff --git a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java index 4c772673..d399f13d 100644 --- a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java +++ b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/PutObjectOperationImpl.java @@ -2,54 +2,51 @@ import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.PutObjectResponse; -import software.amazon.smithy.java.server.RequestContext; import software.amazon.encryption.s3.model.GenericServerError; import software.amazon.encryption.s3.model.PutObjectInput; import software.amazon.encryption.s3.model.PutObjectOutput; import software.amazon.encryption.s3.service.PutObjectOperation; +import software.amazon.smithy.java.server.RequestContext; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.ArrayList; import java.util.Map; -import java.util.stream.Collectors; -import static software.amazon.encryption.s3.S3EncryptionClient.withAdditionalConfiguration; import static software.amazon.encryption.s3.MetadataUtils.metadataListToMap; +import static software.amazon.encryption.s3.S3EncryptionClient.withAdditionalConfiguration; public class PutObjectOperationImpl implements PutObjectOperation { - private Map clientCache_; + private final Map clientCache_; - public PutObjectOperationImpl(Map clientCache) { - clientCache_ = clientCache; - } + public PutObjectOperationImpl(Map clientCache) { + clientCache_ = clientCache; + } - @Override - public PutObjectOutput putObject(PutObjectInput input, RequestContext context) { - try { - final Map metadata = metadataListToMap(input.getMetadata()); - S3Client s3Client = clientCache_.get(input.getClientID()); - s3Client.putObject(builder -> builder - .bucket(input.getBucket()) - .key(input.getKey()) - .overrideConfiguration(withAdditionalConfiguration(metadata)), - RequestBody.fromByteBuffer(input.getBody()) - ); - // The real S3 doesn't provide bucket/key/metadata, so Test doesn't need to either, but we do anyway - return PutObjectOutput.builder() - .bucket(input.getBucket()) - .key(input.getKey()) - .metadata(input.getMetadata()) - .build(); - } catch (Exception e) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String stackTrace = sw.toString(); - throw GenericServerError.builder() - .message(stackTrace) - .build(); + @Override + public PutObjectOutput putObject(PutObjectInput input, RequestContext context) { + try { + final Map metadata = metadataListToMap(input.getMetadata()); + S3Client s3Client = clientCache_.get(input.getClientID()); + s3Client.putObject(builder -> builder + .bucket(input.getBucket()) + .key(input.getKey()) + .overrideConfiguration(withAdditionalConfiguration(metadata)), + RequestBody.fromByteBuffer(input.getBody()) + ); + // The real S3 doesn't provide bucket/key/metadata, so Test doesn't need to either, but we do anyway + return PutObjectOutput.builder() + .bucket(input.getBucket()) + .key(input.getKey()) + .metadata(input.getMetadata()) + .build(); + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String stackTrace = sw.toString(); + throw GenericServerError.builder() + .message(stackTrace) + .build(); + } } - } } diff --git a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java index c3fee4f1..d394b72b 100644 --- a/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java +++ b/test-server/java-v4-server/src/main/java/software/amazon/encryption/s3/S3ECJavaTestServer.java @@ -6,13 +6,13 @@ package software.amazon.encryption.s3; import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.encryption.s3.service.S3ECTestServer; +import software.amazon.smithy.java.server.Server; import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; -import software.amazon.smithy.java.server.Server; -import software.amazon.encryption.s3.service.S3ECTestServer; public class S3ECJavaTestServer implements Runnable { static final URI endpoint = URI.create("http://localhost:8088"); @@ -29,14 +29,14 @@ public void run() { Map clientCache = new ConcurrentHashMap<>(); Server server = Server.builder() - .endpoints(endpoint) - .addService( - S3ECTestServer.builder() - .addCreateClientOperation(new CreateClientOperationImpl(clientCache)) - .addGetObjectOperation(new GetObjectOperationImpl(clientCache)) - .addPutObjectOperation(new PutObjectOperationImpl(clientCache)) - .build()) - .build(); + .endpoints(endpoint) + .addService( + S3ECTestServer.builder() + .addCreateClientOperation(new CreateClientOperationImpl(clientCache)) + .addGetObjectOperation(new GetObjectOperationImpl(clientCache)) + .addPutObjectOperation(new PutObjectOperationImpl(clientCache)) + .build()) + .build(); System.out.println("Starting server..."); server.start(); try {