Skip to content

Commit 50d77c7

Browse files
committed
Add tests
1 parent 8722b24 commit 50d77c7

File tree

4 files changed

+679
-46
lines changed

4 files changed

+679
-46
lines changed
Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
/*
2+
* Copyright 2024 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.it.testcontainers.crypto;
15+
16+
import io.dapr.client.DaprClientBuilder;
17+
import io.dapr.client.DaprPreviewClient;
18+
import io.dapr.client.domain.DecryptRequestAlpha1;
19+
import io.dapr.client.domain.EncryptRequestAlpha1;
20+
import io.dapr.config.Properties;
21+
import io.dapr.testcontainers.Component;
22+
import io.dapr.testcontainers.DaprContainer;
23+
import io.dapr.testcontainers.MetadataEntry;
24+
import org.junit.jupiter.api.AfterAll;
25+
import org.junit.jupiter.api.BeforeAll;
26+
import org.junit.jupiter.api.Tag;
27+
import org.junit.jupiter.api.Test;
28+
import org.testcontainers.containers.BindMode;
29+
import org.testcontainers.junit.jupiter.Container;
30+
import org.testcontainers.junit.jupiter.Testcontainers;
31+
import reactor.core.publisher.Flux;
32+
33+
import java.io.IOException;
34+
import java.nio.charset.StandardCharsets;
35+
import java.nio.file.Files;
36+
import java.nio.file.Path;
37+
import java.security.KeyPair;
38+
import java.security.KeyPairGenerator;
39+
import java.security.NoSuchAlgorithmException;
40+
import java.util.Base64;
41+
import java.util.Collections;
42+
import java.util.List;
43+
import java.util.Random;
44+
45+
import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
46+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
47+
import static org.junit.jupiter.api.Assertions.assertEquals;
48+
import static org.junit.jupiter.api.Assertions.assertNotNull;
49+
import static org.junit.jupiter.api.Assertions.assertTrue;
50+
51+
/**
52+
* Integration tests for the Dapr Cryptography Alpha1 API.
53+
*/
54+
@Testcontainers
55+
@Tag("testcontainers")
56+
public class DaprPreviewClientCryptoIT {
57+
58+
private static final String CRYPTO_COMPONENT_NAME = "localstoragecrypto";
59+
private static final String KEY_NAME = "testkey";
60+
private static final String CONTAINER_KEYS_PATH = "/keys";
61+
62+
private static Path tempKeysDir;
63+
private static DaprPreviewClient daprPreviewClient;
64+
65+
@Container
66+
private static final DaprContainer DAPR_CONTAINER = createDaprContainer();
67+
68+
private static DaprContainer createDaprContainer() {
69+
try {
70+
// Create temporary directory for keys
71+
tempKeysDir = Files.createTempDirectory("dapr-crypto-keys");
72+
73+
// Generate and save a test RSA key pair in PEM format
74+
generateAndSaveRsaKeyPair(tempKeysDir);
75+
76+
// Create the crypto component
77+
Component cryptoComponent = new Component(
78+
CRYPTO_COMPONENT_NAME,
79+
"crypto.dapr.localstorage",
80+
"v1",
81+
List.of(new MetadataEntry("path", CONTAINER_KEYS_PATH))
82+
);
83+
84+
return new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
85+
.withAppName("crypto-test-app")
86+
.withComponent(cryptoComponent)
87+
.withFileSystemBind(tempKeysDir.toString(), CONTAINER_KEYS_PATH, BindMode.READ_ONLY);
88+
89+
} catch (Exception e) {
90+
throw new RuntimeException("Failed to initialize test container", e);
91+
}
92+
}
93+
94+
private static void generateAndSaveRsaKeyPair(Path keysDir) throws NoSuchAlgorithmException, IOException {
95+
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
96+
keyGen.initialize(4096);
97+
KeyPair keyPair = keyGen.generateKeyPair();
98+
99+
// Save the private key in PEM format
100+
String privateKeyPem = "-----BEGIN PRIVATE KEY-----\n" +
101+
Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(keyPair.getPrivate().getEncoded()) +
102+
"\n-----END PRIVATE KEY-----\n";
103+
104+
// Save the public key in PEM format
105+
String publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
106+
Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(keyPair.getPublic().getEncoded()) +
107+
"\n-----END PUBLIC KEY-----\n";
108+
109+
// Combine both keys in one PEM file
110+
String combinedPem = privateKeyPem + publicKeyPem;
111+
112+
Path keyFile = keysDir.resolve(KEY_NAME);
113+
Files.writeString(keyFile, combinedPem);
114+
}
115+
116+
@BeforeAll
117+
static void setUp() {
118+
daprPreviewClient = new DaprClientBuilder()
119+
.withPropertyOverride(Properties.HTTP_ENDPOINT, DAPR_CONTAINER.getHttpEndpoint())
120+
.withPropertyOverride(Properties.GRPC_ENDPOINT, DAPR_CONTAINER.getGrpcEndpoint())
121+
.buildPreviewClient();
122+
}
123+
124+
@AfterAll
125+
static void tearDown() throws Exception {
126+
if (daprPreviewClient != null) {
127+
daprPreviewClient.close();
128+
}
129+
// Clean up temp keys directory
130+
if (tempKeysDir != null && Files.exists(tempKeysDir)) {
131+
Files.walk(tempKeysDir)
132+
.sorted((a, b) -> -a.compareTo(b))
133+
.forEach(path -> {
134+
try {
135+
Files.delete(path);
136+
} catch (IOException e) {
137+
// Ignore cleanup errors
138+
}
139+
});
140+
}
141+
}
142+
143+
@Test
144+
public void testEncryptAndDecryptSmallData() {
145+
String originalData = "Hello, World! This is a test message.";
146+
byte[] plainText = originalData.getBytes(StandardCharsets.UTF_8);
147+
148+
// Encrypt
149+
EncryptRequestAlpha1 encryptRequest = new EncryptRequestAlpha1(
150+
CRYPTO_COMPONENT_NAME,
151+
Flux.just(plainText),
152+
KEY_NAME,
153+
"RSA-OAEP-256"
154+
);
155+
156+
byte[] encryptedData = daprPreviewClient.encrypt(encryptRequest)
157+
.collectList()
158+
.map(chunks -> {
159+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
160+
byte[] result = new byte[totalSize];
161+
int pos = 0;
162+
for (byte[] chunk : chunks) {
163+
System.arraycopy(chunk, 0, result, pos, chunk.length);
164+
pos += chunk.length;
165+
}
166+
return result;
167+
})
168+
.block();
169+
170+
assertNotNull(encryptedData);
171+
assertTrue(encryptedData.length > 0);
172+
173+
// Decrypt
174+
DecryptRequestAlpha1 decryptRequest = new DecryptRequestAlpha1(
175+
CRYPTO_COMPONENT_NAME,
176+
Flux.just(encryptedData)
177+
);
178+
179+
byte[] decryptedData = daprPreviewClient.decrypt(decryptRequest)
180+
.collectList()
181+
.map(chunks -> {
182+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
183+
byte[] result = new byte[totalSize];
184+
int pos = 0;
185+
for (byte[] chunk : chunks) {
186+
System.arraycopy(chunk, 0, result, pos, chunk.length);
187+
pos += chunk.length;
188+
}
189+
return result;
190+
})
191+
.block();
192+
193+
assertNotNull(decryptedData);
194+
assertArrayEquals(plainText, decryptedData);
195+
assertEquals(originalData, new String(decryptedData, StandardCharsets.UTF_8));
196+
}
197+
198+
@Test
199+
public void testEncryptAndDecryptLargeData() {
200+
// Generate a large data payload (1MB)
201+
byte[] largeData = new byte[1024 * 1024];
202+
new Random().nextBytes(largeData);
203+
204+
// Encrypt
205+
EncryptRequestAlpha1 encryptRequest = new EncryptRequestAlpha1(
206+
CRYPTO_COMPONENT_NAME,
207+
Flux.just(largeData),
208+
KEY_NAME,
209+
"RSA-OAEP-256"
210+
);
211+
212+
byte[] encryptedData = daprPreviewClient.encrypt(encryptRequest)
213+
.collectList()
214+
.map(chunks -> {
215+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
216+
byte[] result = new byte[totalSize];
217+
int pos = 0;
218+
for (byte[] chunk : chunks) {
219+
System.arraycopy(chunk, 0, result, pos, chunk.length);
220+
pos += chunk.length;
221+
}
222+
return result;
223+
})
224+
.block();
225+
226+
assertNotNull(encryptedData);
227+
assertTrue(encryptedData.length > 0);
228+
229+
// Decrypt
230+
DecryptRequestAlpha1 decryptRequest = new DecryptRequestAlpha1(
231+
CRYPTO_COMPONENT_NAME,
232+
Flux.just(encryptedData)
233+
);
234+
235+
byte[] decryptedData = daprPreviewClient.decrypt(decryptRequest)
236+
.collectList()
237+
.map(chunks -> {
238+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
239+
byte[] result = new byte[totalSize];
240+
int pos = 0;
241+
for (byte[] chunk : chunks) {
242+
System.arraycopy(chunk, 0, result, pos, chunk.length);
243+
pos += chunk.length;
244+
}
245+
return result;
246+
})
247+
.block();
248+
249+
assertNotNull(decryptedData);
250+
assertArrayEquals(largeData, decryptedData);
251+
}
252+
253+
@Test
254+
public void testEncryptAndDecryptStreamedData() {
255+
// Create chunked data to simulate streaming
256+
byte[] chunk1 = "First chunk of data. ".getBytes(StandardCharsets.UTF_8);
257+
byte[] chunk2 = "Second chunk of data. ".getBytes(StandardCharsets.UTF_8);
258+
byte[] chunk3 = "Third and final chunk.".getBytes(StandardCharsets.UTF_8);
259+
260+
// Combine for comparison later
261+
byte[] fullData = new byte[chunk1.length + chunk2.length + chunk3.length];
262+
System.arraycopy(chunk1, 0, fullData, 0, chunk1.length);
263+
System.arraycopy(chunk2, 0, fullData, chunk1.length, chunk2.length);
264+
System.arraycopy(chunk3, 0, fullData, chunk1.length + chunk2.length, chunk3.length);
265+
266+
// Encrypt with multiple chunks
267+
EncryptRequestAlpha1 encryptRequest = new EncryptRequestAlpha1(
268+
CRYPTO_COMPONENT_NAME,
269+
Flux.just(chunk1, chunk2, chunk3),
270+
KEY_NAME,
271+
"RSA-OAEP-256"
272+
);
273+
274+
byte[] encryptedData = daprPreviewClient.encrypt(encryptRequest)
275+
.collectList()
276+
.map(chunks -> {
277+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
278+
byte[] result = new byte[totalSize];
279+
int pos = 0;
280+
for (byte[] chunk : chunks) {
281+
System.arraycopy(chunk, 0, result, pos, chunk.length);
282+
pos += chunk.length;
283+
}
284+
return result;
285+
})
286+
.block();
287+
288+
assertNotNull(encryptedData);
289+
assertTrue(encryptedData.length > 0);
290+
291+
// Decrypt
292+
DecryptRequestAlpha1 decryptRequest = new DecryptRequestAlpha1(
293+
CRYPTO_COMPONENT_NAME,
294+
Flux.just(encryptedData)
295+
);
296+
297+
byte[] decryptedData = daprPreviewClient.decrypt(decryptRequest)
298+
.collectList()
299+
.map(chunks -> {
300+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
301+
byte[] result = new byte[totalSize];
302+
int pos = 0;
303+
for (byte[] chunk : chunks) {
304+
System.arraycopy(chunk, 0, result, pos, chunk.length);
305+
pos += chunk.length;
306+
}
307+
return result;
308+
})
309+
.block();
310+
311+
assertNotNull(decryptedData);
312+
assertArrayEquals(fullData, decryptedData);
313+
}
314+
315+
@Test
316+
public void testEncryptWithOptionalParameters() {
317+
String originalData = "Test message with optional parameters.";
318+
byte[] plainText = originalData.getBytes(StandardCharsets.UTF_8);
319+
320+
// Encrypt with optional data encryption cipher
321+
EncryptRequestAlpha1 encryptRequest = new EncryptRequestAlpha1(
322+
CRYPTO_COMPONENT_NAME,
323+
Flux.just(plainText),
324+
KEY_NAME,
325+
"RSA-OAEP-256"
326+
).setDataEncryptionCipher("aes-gcm");
327+
328+
byte[] encryptedData = daprPreviewClient.encrypt(encryptRequest)
329+
.collectList()
330+
.map(chunks -> {
331+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
332+
byte[] result = new byte[totalSize];
333+
int pos = 0;
334+
for (byte[] chunk : chunks) {
335+
System.arraycopy(chunk, 0, result, pos, chunk.length);
336+
pos += chunk.length;
337+
}
338+
return result;
339+
})
340+
.block();
341+
342+
assertNotNull(encryptedData);
343+
assertTrue(encryptedData.length > 0);
344+
345+
// Decrypt
346+
DecryptRequestAlpha1 decryptRequest = new DecryptRequestAlpha1(
347+
CRYPTO_COMPONENT_NAME,
348+
Flux.just(encryptedData)
349+
);
350+
351+
byte[] decryptedData = daprPreviewClient.decrypt(decryptRequest)
352+
.collectList()
353+
.map(chunks -> {
354+
int totalSize = chunks.stream().mapToInt(chunk -> chunk.length).sum();
355+
byte[] result = new byte[totalSize];
356+
int pos = 0;
357+
for (byte[] chunk : chunks) {
358+
System.arraycopy(chunk, 0, result, pos, chunk.length);
359+
pos += chunk.length;
360+
}
361+
return result;
362+
})
363+
.block();
364+
365+
assertNotNull(decryptedData);
366+
assertArrayEquals(plainText, decryptedData);
367+
}
368+
}

0 commit comments

Comments
 (0)