Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.paimon.utils.Pair;

import com.aliyun.jindodata.common.JindoHadoopSystem;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
Expand Down Expand Up @@ -124,6 +125,7 @@ public void testCreateBlobPresignedUrlUsesOssClient() throws Exception {
"paimon-blob-descriptor-sha256", sha256Hex(descriptor.serialize()));
when(client.headObject(eq("bucket"), contains("_bloburl_"))).thenReturn(metadata);
when(client.getEndpoint()).thenReturn(URI.create("https://oss.example.com"));
when(client.getClientConfiguration()).thenReturn(new ClientConfiguration());
when(client.generatePresignedUrl(eq("bucket"), anyString(), any(), eq(HttpMethod.GET)))
.thenAnswer(
invocation ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,15 @@ private static String sha256Hex(byte[] bytes) {
private static void validatePresignedUrl(
OSSClient client, URL url, String bucket, String targetKey) throws Exception {
URI endpoint = client.getEndpoint();
String expectedHost = bucket + "." + endpoint.getHost();
// With fs.oss.sld.enabled the client signs path-style URLs: the host is the endpoint and
// the bucket becomes the first path segment, so bucket and key are still both checked.
boolean pathStyle = client.getClientConfiguration().isSLDEnabled();
String expectedHost = pathStyle ? endpoint.getHost() : bucket + "." + endpoint.getHost();
String expectedPath = pathStyle ? "/" + bucket + "/" + targetKey : "/" + targetKey;
if (!"https".equalsIgnoreCase(endpoint.getScheme())
|| !"https".equalsIgnoreCase(url.getProtocol())
|| !expectedHost.equalsIgnoreCase(url.getHost())
|| !("/" + targetKey).equals(url.toURI().getPath())) {
|| !expectedPath.equals(url.toURI().getPath())) {
throw new IOException("OSS client generated a presigned URL for an invalid target.");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public void testCreateBlobPresignedUrlMaterializesSinglePart() throws Exception
copied.setPartNumber(1);
copied.setETag("etag");
when(client.uploadPartCopy(any())).thenReturn(copied);
when(client.getClientConfiguration()).thenReturn(new ClientConfiguration());
when(client.getEndpoint()).thenReturn(URI.create("https://oss-cn-hangzhou.aliyuncs.com"));
when(client.generatePresignedUrl(eq("bucket"), anyString(), any(), eq(HttpMethod.GET)))
.thenAnswer(
Expand Down Expand Up @@ -158,6 +159,7 @@ public void testCreateBlobPresignedUrlPreservesInternalEndpoint() throws Excepti
new BlobDescriptor("oss://bucket/table/-internal.aliyuncs.com/source.blob", 0, 1);
when(client.headObject(eq("bucket"), contains("_bloburl_")))
.thenReturn(matchingMetadata(descriptor));
when(client.getClientConfiguration()).thenReturn(new ClientConfiguration());
when(client.getEndpoint())
.thenReturn(URI.create("https://oss-cn-hangzhou-internal.aliyuncs.com"));
when(client.generatePresignedUrl(eq("bucket"), anyString(), any(), eq(HttpMethod.GET)))
Expand Down Expand Up @@ -337,11 +339,112 @@ public void testCreateBlobPresignedUrlRejectsInvalidTarget() throws Exception {
"https://bucket.oss-cn-hangzhou.aliyuncs.com/table/_bloburl_hash");
}

@Test
public void testCreateBlobPresignedUrlAcceptsPathStyleWhenSldEnabled() throws Exception {
BlobDescriptor descriptor = new BlobDescriptor("oss://bucket/table/source.blob", 0, 1);
String key = "table/_bloburl_" + sha256Hex(descriptor.serialize());
for (String endpoint :
new String[] {
"https://oss-cn-hangzhou.aliyuncs.com",
"https://oss-cn-hangzhou-internal.aliyuncs.com",
"https://ep-abc.oss.cn-hangzhou.privatelink.aliyuncs.com"
}) {
String signed = endpoint + "/bucket/" + key + "?Signature=test";
assertThat(presignWithSldMock(endpoint, descriptor, signed)).isEqualTo(signed);
}

// The bucket is still verified, now as the first path segment.
assertThatThrownBy(
() ->
presignWithSldMock(
"https://oss-cn-hangzhou.aliyuncs.com",
descriptor,
"https://oss-cn-hangzhou.aliyuncs.com/other/" + key))
.isInstanceOf(java.io.IOException.class)
.hasMessageContaining("invalid target");
}

@Test
public void testCreateBlobPresignedUrlAcceptsSdkPathStyleUrlWithEncodedKey() throws Exception {
BlobDescriptor descriptor =
new BlobDescriptor("oss://bucket/table/a b/\uD55C/source.blob", 0, 1);

URL url = new URL(presignWithSldClient("https://oss-cn-hangzhou.aliyuncs.com", descriptor));

assertThat(url.getHost()).isEqualTo("oss-cn-hangzhou.aliyuncs.com");
assertThat(url.toURI().getPath())
.isEqualTo(
"/bucket/table/a b/\uD55C/_bloburl_" + sha256Hex(descriptor.serialize()));
}

@Test
public void testCreateBlobPresignedUrlAcceptsSdkPathStyleUrlWithEndpointPort()
throws Exception {
BlobDescriptor descriptor = new BlobDescriptor("oss://bucket/table/source.blob", 0, 1);

URL url =
new URL(
presignWithSldClient(
"https://oss-cn-hangzhou.aliyuncs.com:8443", descriptor));

assertThat(url.getHost()).isEqualTo("oss-cn-hangzhou.aliyuncs.com");
assertThat(url.getPort()).isEqualTo(8443);
assertThat(url.getPath())
.isEqualTo("/bucket/table/_bloburl_" + sha256Hex(descriptor.serialize()));
}

private static String presignWithSldMock(
String endpoint, BlobDescriptor descriptor, String generatedUrl) throws Exception {
OSSClient client = mock(OSSClient.class);
when(client.headObject(eq("bucket"), contains("_bloburl_")))
.thenReturn(matchingMetadata(descriptor));
ClientConfiguration configuration = new ClientConfiguration();
configuration.setSLDEnabled(true);
when(client.getClientConfiguration()).thenReturn(configuration);
when(client.getEndpoint()).thenReturn(URI.create(endpoint));
when(client.generatePresignedUrl(eq("bucket"), anyString(), any(), eq(HttpMethod.GET)))
.thenReturn(presignedUrl(generatedUrl));
return new TestOSSFileIO(client)
.createBlobPresignedUrl(
new Path("oss://bucket/table"), descriptor, Duration.ofMinutes(5));
}

/**
* Presigns with the URL the SDK itself signs once {@code fs.oss.sld.enabled} has turned on
* second-level domain mode, so the validator is checked against the real path-style output.
*/
private static String presignWithSldClient(String endpoint, BlobDescriptor descriptor)
throws Exception {
OSSClient signer = hadoopStyleClient(endpoint);
try {
signer.getClientConfiguration().setSLDEnabled(true);
OSSClient client = mock(OSSClient.class);
when(client.headObject(eq("bucket"), contains("_bloburl_")))
.thenReturn(matchingMetadata(descriptor));
when(client.getClientConfiguration()).thenReturn(signer.getClientConfiguration());
when(client.getEndpoint()).thenReturn(signer.getEndpoint());
when(client.generatePresignedUrl(eq("bucket"), anyString(), any(), eq(HttpMethod.GET)))
.thenAnswer(
invocation ->
signer.generatePresignedUrl(
"bucket",
invocation.getArgument(1),
invocation.getArgument(2),
HttpMethod.GET));
return new TestOSSFileIO(client)
.createBlobPresignedUrl(
new Path("oss://bucket/table"), descriptor, Duration.ofMinutes(5));
} finally {
signer.shutdown();
}
}

private static void assertInvalidPresignedUrl(String endpoint, String generatedUrl) {
OSSClient client = mock(OSSClient.class);
BlobDescriptor descriptor = new BlobDescriptor("oss://bucket/table/source.blob", 0, 1);
when(client.headObject(eq("bucket"), contains("_bloburl_")))
.thenReturn(matchingMetadata(descriptor));
when(client.getClientConfiguration()).thenReturn(new ClientConfiguration());
when(client.getEndpoint()).thenReturn(URI.create(endpoint));
when(client.generatePresignedUrl(eq("bucket"), anyString(), any(), eq(HttpMethod.GET)))
.thenReturn(presignedUrl(generatedUrl));
Expand Down Expand Up @@ -407,6 +510,7 @@ private static void stubMultipartUpload(OSSClient client) {
}

private static void stubPresigning(OSSClient client) {
when(client.getClientConfiguration()).thenReturn(new ClientConfiguration());
when(client.getEndpoint()).thenReturn(URI.create("https://oss-cn-hangzhou.aliyuncs.com"));
when(client.generatePresignedUrl(eq("bucket"), anyString(), any(), eq(HttpMethod.GET)))
.thenAnswer(
Expand Down
Loading