Skip to content

Commit 410fb9c

Browse files
authored
accept sas token with leading ? in connection string. (Azure#30003)
1 parent ac66969 commit 410fb9c

File tree

4 files changed

+191
-0
lines changed

4 files changed

+191
-0
lines changed

sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/SasClientTests.groovy

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,36 @@ class SasClientTests extends APISpec {
124124
validateSasProperties(properties)
125125
}
126126

127+
def "can use connection string with sas and question mark"() {
128+
setup:
129+
def permissions = new BlobSasPermission()
130+
.setReadPermission(true)
131+
132+
def sasValues = generateValues(permissions)
133+
134+
when:
135+
def sas = sasClient.generateSas(sasValues)
136+
137+
def connectionString = String.format("BlobEndpoint=%s;SharedAccessSignature=%s;",
138+
environment.primaryAccount.blobEndpoint,
139+
"?" + sas)
140+
141+
def client = instrument(new BlobClientBuilder())
142+
.connectionString(connectionString)
143+
.containerName(sasClient.getContainerName())
144+
.blobName(sasClient.getBlobName())
145+
.buildClient()
146+
147+
def os = new ByteArrayOutputStream()
148+
client.downloadStream(os)
149+
def properties = client.getProperties()
150+
151+
then:
152+
notThrown(BlobStorageException)
153+
os.toString() == data.defaultText
154+
validateSasProperties(properties)
155+
}
156+
127157
def "container sas identifier and permissions"() {
128158
setup:
129159
def identifier = new BlobSignedIdentifier()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
{
2+
"networkCallRecords" : [ {
3+
"Method" : "PUT",
4+
"Uri" : "https://REDACTED.blob.core.windows.net/9bd8428b09bd8428bdea2192933527635fdd941088c7?restype=container",
5+
"Headers" : {
6+
"x-ms-version" : "2021-08-06",
7+
"User-Agent" : "azsdk-java-azure-storage-blob/12.19.0-beta.1 (17; Windows 10; 10.0)",
8+
"x-ms-client-request-id" : "ccb40785-4977-4f5b-8285-b96cb10dea5c"
9+
},
10+
"Response" : {
11+
"content-length" : "0",
12+
"x-ms-version" : "2021-08-06",
13+
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
14+
"eTag" : "0x8DA68F277C0B00B",
15+
"Last-Modified" : "Mon, 18 Jul 2022 19:19:42 GMT",
16+
"retry-after" : "0",
17+
"StatusCode" : "201",
18+
"x-ms-request-id" : "83b5c48c-501e-0054-05db-9a85e6000000",
19+
"x-ms-client-request-id" : "ccb40785-4977-4f5b-8285-b96cb10dea5c",
20+
"Date" : "Mon, 18 Jul 2022 19:19:42 GMT"
21+
},
22+
"Exception" : null
23+
}, {
24+
"Method" : "PUT",
25+
"Uri" : "https://REDACTED.blob.core.windows.net/9bd8428b09bd8428bdea2192933527635fdd941088c7/9bd8428b19bd8428bdea8307974edd0a91f7649c39f3",
26+
"Headers" : {
27+
"x-ms-version" : "2021-08-06",
28+
"User-Agent" : "azsdk-java-azure-storage-blob/12.19.0-beta.1 (17; Windows 10; 10.0)",
29+
"x-ms-client-request-id" : "36359e7c-9e8c-4d79-8446-c1f99a5af10f",
30+
"Content-Type" : "application/octet-stream"
31+
},
32+
"Response" : {
33+
"content-length" : "0",
34+
"x-ms-version" : "2021-08-06",
35+
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
36+
"x-ms-content-crc64" : "6RYQPwaVsyQ=",
37+
"Last-Modified" : "Mon, 18 Jul 2022 19:19:43 GMT",
38+
"retry-after" : "0",
39+
"StatusCode" : "201",
40+
"x-ms-request-server-encrypted" : "true",
41+
"Date" : "Mon, 18 Jul 2022 19:19:42 GMT",
42+
"Content-MD5" : "wh+Wm18D0z1D4E+PE252gg==",
43+
"eTag" : "0x8DA68F2780704F2",
44+
"x-ms-request-id" : "83b5c761-501e-0054-08db-9a85e6000000",
45+
"x-ms-client-request-id" : "36359e7c-9e8c-4d79-8446-c1f99a5af10f"
46+
},
47+
"Exception" : null
48+
}, {
49+
"Method" : "GET",
50+
"Uri" : "https://REDACTED.blob.core.windows.net/9bd8428b09bd8428bdea2192933527635fdd941088c7/9bd8428b19bd8428bdea8307974edd0a91f7649c39f3?sv=2021-08-06&spr=https%2Chttp&st=2022-07-17T19%3A19%3A43Z&se=2022-07-19T19%3A19%3A43Z&sp=r&sig=REDACTED&sr=b&rscc=cache&rscd=disposition&rsce=encoding&rscl=language&rsct=type",
51+
"Headers" : {
52+
"x-ms-version" : "2021-08-06",
53+
"User-Agent" : "azsdk-java-azure-storage-blob/12.19.0-beta.1 (17; Windows 10; 10.0)",
54+
"x-ms-client-request-id" : "15a7aeae-ad17-497d-bf3a-19bdb3488bc1"
55+
},
56+
"Response" : {
57+
"content-length" : "7",
58+
"x-ms-last-access-time" : "Mon, 18 Jul 2022 19:19:43 GMT",
59+
"x-ms-version" : "2021-08-06",
60+
"x-ms-lease-status" : "unlocked",
61+
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
62+
"x-ms-lease-state" : "available",
63+
"Last-Modified" : "Mon, 18 Jul 2022 19:19:43 GMT",
64+
"retry-after" : "0",
65+
"StatusCode" : "200",
66+
"Date" : "Mon, 18 Jul 2022 19:19:43 GMT",
67+
"Content-MD5" : "wh+Wm18D0z1D4E+PE252gg==",
68+
"x-ms-blob-type" : "BlockBlob",
69+
"Accept-Ranges" : "bytes",
70+
"x-ms-server-encrypted" : "true",
71+
"Cache-Control" : "cache",
72+
"Content-Disposition" : "disposition",
73+
"x-ms-creation-time" : "Mon, 18 Jul 2022 19:19:43 GMT",
74+
"eTag" : "0x8DA68F2780704F2",
75+
"Content-Length" : "7",
76+
"x-ms-request-id" : "83b5c8c2-501e-0054-47db-9a85e6000000",
77+
"Body" : "default",
78+
"Content-Language" : "language",
79+
"x-ms-client-request-id" : "15a7aeae-ad17-497d-bf3a-19bdb3488bc1",
80+
"Content-Type" : "type"
81+
},
82+
"Exception" : null
83+
}, {
84+
"Method" : "HEAD",
85+
"Uri" : "https://REDACTED.blob.core.windows.net/9bd8428b09bd8428bdea2192933527635fdd941088c7/9bd8428b19bd8428bdea8307974edd0a91f7649c39f3?sv=2021-08-06&spr=https%2Chttp&st=2022-07-17T19%3A19%3A43Z&se=2022-07-19T19%3A19%3A43Z&sp=r&sig=REDACTED&sr=b&rscc=cache&rscd=disposition&rsce=encoding&rscl=language&rsct=type",
86+
"Headers" : {
87+
"x-ms-version" : "2021-08-06",
88+
"User-Agent" : "azsdk-java-azure-storage-blob/12.19.0-beta.1 (17; Windows 10; 10.0)",
89+
"x-ms-client-request-id" : "236592bb-c41b-4765-acae-b410e6590573"
90+
},
91+
"Response" : {
92+
"content-length" : "7",
93+
"x-ms-lease-status" : "unlocked",
94+
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
95+
"x-ms-lease-state" : "available",
96+
"Last-Modified" : "Mon, 18 Jul 2022 19:19:43 GMT",
97+
"retry-after" : "0",
98+
"StatusCode" : "200",
99+
"x-ms-blob-type" : "BlockBlob",
100+
"x-ms-access-tier-inferred" : "true",
101+
"x-ms-access-tier" : "Hot",
102+
"Content-Encoding" : "encoding",
103+
"x-ms-creation-time" : "Mon, 18 Jul 2022 19:19:43 GMT",
104+
"eTag" : "0x8DA68F2780704F2",
105+
"x-ms-request-id" : "83b5ca14-501e-0054-7edb-9a85e6000000",
106+
"Content-Type" : "type",
107+
"x-ms-last-access-time" : "Mon, 18 Jul 2022 19:19:43 GMT",
108+
"x-ms-version" : "2021-08-06",
109+
"Date" : "Mon, 18 Jul 2022 19:19:43 GMT",
110+
"Content-MD5" : "wh+Wm18D0z1D4E+PE252gg==",
111+
"Accept-Ranges" : "bytes",
112+
"x-ms-server-encrypted" : "true",
113+
"Cache-Control" : "cache",
114+
"Content-Disposition" : "disposition",
115+
"Content-Language" : "language",
116+
"x-ms-client-request-id" : "236592bb-c41b-4765-acae-b410e6590573"
117+
},
118+
"Exception" : null
119+
} ],
120+
"variables" : [ "9bd8428b09bd8428bdea2192933527635fdd941088c7", "9bd8428b19bd8428bdea8307974edd0a91f7649c39f3", "2022-07-18T19:19:43.254879300Z", "2022-07-18T19:19:43.265875400Z" ]
121+
}

sdk/storage/azure-storage-common/src/main/java/com/azure/storage/common/implementation/SasImplUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ public static Map<String, String[]> parseQueryString(String queryParams) {
8282
return retVals;
8383
}
8484

85+
// trim leading ? if present.
86+
if (queryParams.startsWith("?")) {
87+
queryParams = queryParams.substring(1);
88+
}
89+
8590
// split name value pairs by splitting on the '&' character
8691
final String[] valuePairs = queryParams.split("&");
8792

sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/connectionstring/StorageConnectionStringTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,41 @@ public void sasToken() {
6161
Assertions.assertNull(storageConnectionString.getAccountName());
6262
}
6363

64+
@Test
65+
public void sasTokenWithQuestionMark() {
66+
final String blobEndpointStr = "https://storagesample.blob.core.windows.net";
67+
final String fileEndpointStr = "https://storagesample.file.core.windows.net";
68+
69+
final String connectionString = String.format("BlobEndpoint=%s;FileEndpoint=%s;SharedAccessSignature=%s;",
70+
blobEndpointStr,
71+
fileEndpointStr,
72+
"?" + SAS_TOKEN);
73+
74+
StorageConnectionString storageConnectionString = StorageConnectionString.create(connectionString, logger);
75+
Assertions.assertNotNull(storageConnectionString);
76+
StorageEndpoint blobEndpoint = storageConnectionString.getBlobEndpoint();
77+
Assertions.assertNotNull(blobEndpoint);
78+
Assertions.assertNotNull(blobEndpoint.getPrimaryUri());
79+
Assertions.assertTrue(blobEndpoint.getPrimaryUri().equalsIgnoreCase(blobEndpointStr));
80+
81+
StorageEndpoint fileEndpoint = storageConnectionString.getFileEndpoint();
82+
Assertions.assertNotNull(fileEndpoint);
83+
Assertions.assertNotNull(fileEndpoint.getPrimaryUri());
84+
Assertions.assertTrue(fileEndpoint.getPrimaryUri().equalsIgnoreCase(fileEndpointStr));
85+
86+
Assertions.assertNull(storageConnectionString.getQueueEndpoint());
87+
Assertions.assertNull(storageConnectionString.getTableEndpoint());
88+
89+
StorageAuthenticationSettings authSettings
90+
= storageConnectionString.getStorageAuthSettings();
91+
Assertions.assertNotNull(authSettings);
92+
Assertions.assertEquals(StorageAuthenticationSettings.Type.SAS_TOKEN,
93+
authSettings.getType());
94+
Assertions.assertNotNull(authSettings.getSasToken());
95+
assertSasTokensEqual(authSettings.getSasToken(), SAS_TOKEN);
96+
Assertions.assertNull(storageConnectionString.getAccountName());
97+
}
98+
6499
private static void assertSasTokensEqual(String left, String right) {
65100
Map<String, String[]> leftMap = SasImplUtils.parseQueryString(left);
66101
Map<String, String[]> rightMap = SasImplUtils.parseQueryString(right);

0 commit comments

Comments
 (0)