Skip to content

Commit

Permalink
S3: Format x-amz-date with year-of-era (#679)
Browse files Browse the repository at this point in the history
"YYYY" in `DateTimeFormatter.ofPattern("YYYYMMdd")` means
week-based-year, which formats `LocalDate.of(2017, 12, 31)` as
"20181231" and results in unexpected rejections from S3 as below:

> The difference between the request time and the current time is
too large.

Replacing it with "yyyy" fixes the bug.
  • Loading branch information
guersam authored and ktoso committed Dec 31, 2017
1 parent 52f5c06 commit 6f12b2d
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
2 changes: 1 addition & 1 deletion s3/src/main/scala/akka/stream/alpakka/s3/auth/Signer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.amazonaws.auth
import com.amazonaws.auth._

private[alpakka] object Signer {
private val dateFormatter = DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmmssX")
private val dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX")

def signedRequest(request: HttpRequest, key: SigningKey, date: ZonedDateTime = ZonedDateTime.now(ZoneOffset.UTC))(
implicit mat: Materializer
Expand Down
25 changes: 20 additions & 5 deletions s3/src/test/scala/akka/stream/alpakka/s3/auth/SignerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

package akka.stream.alpakka.s3.auth

import java.time.{LocalDate, LocalDateTime, ZoneOffset}
import java.time.{LocalDate, LocalDateTime, ZoneOffset, ZonedDateTime}

import akka.actor.ActorSystem
import akka.http.scaladsl.model.{HttpMethods, HttpRequest}
import akka.http.scaladsl.model.headers.{Host, RawHeader}
Expand All @@ -26,8 +27,9 @@ class SignerSpec(_system: ActorSystem) extends TestKit(_system) with FlatSpecLik
val credentials = new AWSStaticCredentialsProvider(
new BasicAWSCredentials("AKIDEXAMPLE", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY")
)
val scope = CredentialScope(LocalDate.of(2015, 8, 30), "us-east-1", "iam")
val signingKey = SigningKey(credentials, scope)

def scope(date: LocalDate) = CredentialScope(date, "us-east-1", "iam")
def signingKey(dateTime: ZonedDateTime) = SigningKey(credentials, scope(dateTime.toLocalDate))

val cr = CanonicalRequest(
"GET",
Expand All @@ -40,7 +42,7 @@ class SignerSpec(_system: ActorSystem) extends TestKit(_system) with FlatSpecLik

"Signer" should "calculate the string to sign" in {
val date = LocalDateTime.of(2015, 8, 30, 12, 36, 0).atZone(ZoneOffset.UTC)
val stringToSign: String = Signer.stringToSign("AWS4-HMAC-SHA256", signingKey, date, cr)
val stringToSign: String = Signer.stringToSign("AWS4-HMAC-SHA256", signingKey(date), date, cr)
stringToSign should equal(
"AWS4-HMAC-SHA256\n20150830T123600Z\n20150830/us-east-1/iam/aws4_request\nf536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59"
)
Expand All @@ -54,8 +56,9 @@ class SignerSpec(_system: ActorSystem) extends TestKit(_system) with FlatSpecLik
RawHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
)

val date = LocalDateTime.of(2015, 8, 30, 12, 36, 0).atZone(ZoneOffset.UTC)
val srFuture =
Signer.signedRequest(req, signingKey, LocalDateTime.of(2015, 8, 30, 12, 36, 0).atZone(ZoneOffset.UTC))
Signer.signedRequest(req, signingKey(date), date)
whenReady(srFuture) { signedRequest =>
signedRequest should equal(
HttpRequest(HttpMethods.GET)
Expand All @@ -74,4 +77,16 @@ class SignerSpec(_system: ActorSystem) extends TestKit(_system) with FlatSpecLik
}
}

it should "format x-amz-date based on year-of-era instead of week-based-year" in {
val req = HttpRequest(HttpMethods.GET)
.withUri("https://iam.amazonaws.com/?Action=ListUsers&Version=2010-05-08")

val date = LocalDateTime.of(2017, 12, 31, 12, 36, 0).atZone(ZoneOffset.UTC)
val srFuture =
Signer.signedRequest(req, signingKey(date), date)

whenReady(srFuture) { signedRequest =>
signedRequest.getHeader("x-amz-date").get.value should equal("20171231T123600Z")
}
}
}

0 comments on commit 6f12b2d

Please sign in to comment.