Skip to content

Commit 42e298c

Browse files
authored
Merge pull request #102 from AnyMindGroup/fix/signed-url-path-encoding
fix signed url path encoding with special characters
2 parents 8c98625 + 28c36ab commit 42e298c

File tree

3 files changed

+20
-5
lines changed

3 files changed

+20
-5
lines changed

tests/shared/src/test/scala/V4CanonicalRequestBuilderSpec.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ object V4CanonicalRequestBuilderSpec extends ZIOSpecDefault:
9292
method = Method.GET,
9393
timestamp = Instant.parse("2019-12-01T19:08:59Z"),
9494
// all characters from the docs https://cloud.google.com/storage/docs/authentication/canonical-requests#about-resource-path
95-
resourcePath = List("test", """?=!#$&'()*+,:;@[]".png"""),
95+
resourcePath = List("test", """?=!#$&'()*+,:;@[]"~.png"""),
9696
contentType = Some(MediaType.ImagePng),
9797
bucket = "test-bucket",
9898
serviceAccountEmail = "test@gcp.iam.gserviceaccount.com",
@@ -104,7 +104,7 @@ object V4CanonicalRequestBuilderSpec extends ZIOSpecDefault:
104104
assertTrue(
105105
req.payloadPlain.linesIterator
106106
.drop(1)
107-
.next == """/test-bucket/test/%3F%3D%21%23%24%26%27%28%29*%2B%2C%3A%3B%40%5B%5D%22.png"""
107+
.next == """/test-bucket/test/%3F%3D%21%23%24%26%27%28%29%2A%2B%2C%3A%3B%40%5B%5D%22~.png"""
108108
)
109109
} yield assertCompletes
110110
},

tests/shared/src/test/scala/V4SignUrlRequestBuilderSpec.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object V4SignUrlRequestBuilderSpec extends ZIOSpecDefault:
1414
override def spec: Spec[Any, Any] = suite("V4SignUrlRequestBuilderSpec")(
1515
test("return signed url") {
1616
val testBucket = "example-bucket"
17-
val testResource = List("test", """cat_?=!#$&'()*+,:;@[]".jpeg""")
17+
val testResource = List("test", """cat_?=!#$&'()*+,:;@[]"~.jpeg""")
1818
val signatureResponse =
1919
"PszqKoIc7Q3TU22ouo9YWxtMk9jtKZBWMdqEwKb57+XlritZsYnaBiNhRaE5YvARf421zqS/M2kKPuPbYJc9c2GyEk66Y/J8o2QFpo65tKaFIvADvkBUiNg6IXSs3YL4udg+roLeMPi6r5NJqjp3Rf+7FT6xN8xImtw33DGjbffkp6BhUuHSt9USOCzbDOSmjTAiTuuo5eNUSbL6f5xZroKq07wTj3ETDDICV/QkB6VlxGffi1TVKF14dgrBuE0jwATsWyVBFFVXJ7pB+uUc8UDgZQzAVTjahJUFWAevg9+QgA2HQlc5a0u3Cs+/tJZjWnsx3hm0S8NJqGIOa8mO0w=="
2020
val expectedSignature =
@@ -32,7 +32,7 @@ object V4SignUrlRequestBuilderSpec extends ZIOSpecDefault:
3232
signAlgorithm = V4SignAlgorithm.`GOOG4-RSA-SHA256`,
3333
expiresInSeconds = V4SignatureExpiration.inSeconds(900),
3434
)
35-
expectedPath = "/example-bucket/test/cat_%3F%3D%21%23%24%26%27%28%29*%2B%2C%3A%3B%40%5B%5D%22.jpeg"
35+
expectedPath = "/example-bucket/test/cat_%3F%3D%21%23%24%26%27%28%29%2A%2B%2C%3A%3B%40%5B%5D%22~.jpeg"
3636
_ <- assertTrue(canonicalRequest.payloadPlain.linesIterator.drop(1).next == expectedPath)
3737
signedUrl <- ZIO.fromEither:
3838
V4SignUrlRequestBuilder

zio-gcp-storage/shared/src/main/scala/V4CanonicalRequestBuilder.scala

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import java.time.temporal.ChronoField
77
import java.time.{Instant, ZoneOffset}
88

99
import sttp.model.Uri.{QuerySegmentEncoding, Segment}
10+
import sttp.model.internal.Rfc3986
1011
import sttp.model.{MediaType, Method, QueryParams}
1112

1213
// https://cloud.google.com/storage/docs/authentication/canonical-requests
@@ -109,8 +110,22 @@ private[storage] class V4CanonicalRequestBuilder(
109110
}
110111
}
111112

113+
private[storage] val resourcePathAllowedChars =
114+
Rfc3986.Query --
115+
// https://docs.cloud.google.com/storage/docs/authentication/canonical-requests#about-resource-path
116+
Set('?', '=', '!', '#', '$', '&', '\'', '(', ')', '*', '+', ',', ':', ';', '@', '[', ']')
117+
112118
private[storage] def toResourcePathSegments(resourcePath: Seq[String]) =
113-
resourcePath.map(p => Segment(p, QuerySegmentEncoding.All))
119+
resourcePath.map(p =>
120+
Segment(
121+
p,
122+
Rfc3986.encode(
123+
allowedCharacters = resourcePathAllowedChars,
124+
spaceAsPlus = false,
125+
encodePlus = true,
126+
),
127+
)
128+
)
114129

115130
private[storage] object V4CanonicalRequestBuilder:
116131
def apply(): V4CanonicalRequestBuilder =

0 commit comments

Comments
 (0)