Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#98 - end to end testing of AWS Signature 4 based on AWS test suite #120

Closed
wants to merge 6 commits into from

Conversation

devsprint
Copy link

…t validation.

@lightbend-cla-validator

Hi @devsprint,

Thank you for your contribution! We really value the time you've taken to put this together.

Before we proceed with reviewing this pull request, please sign the Lightbend Contributors License Agreement:

http://www.lightbend.com/contribute/cla

@ktoso
Copy link
Member

ktoso commented Dec 16, 2016

Refs #98
Thanks, not sure if I'll be able to review today but hope someone might be able to :)

@devsprint
Copy link
Author

CLA signed

@devsprint devsprint changed the title #98 WIP - implemented the validation for first step. Canonical Reques… #98 - end to end testing of AWS Signature 4 based on AWS test suite Dec 21, 2016
Copy link
Member

@patriknw patriknw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@patriknw
Copy link
Member

@agolubev is this something you would like to review?

@agolubev
Copy link
Contributor

@patriknw thanks for the notification. I'll review this today

Copy link
Contributor

@agolubev agolubev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR has a lot of header manipulation and seems like 3 or 4 akka-http problems were identified. Would be good to have them as tickets with ticket number in code at least

private def uriEncode(str: String) = URLEncoder.encode(str, "utf-8")
private def encodeQueryParams(name: String, value: String): String =
if (name.contains(' ')) {
s"${uriEncode(name.takeWhile(_ != ' ')).replace("%7E", "~")}="
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be changed to just string append?


private def uriEncode(str: String) = if (isAlreadyURLEncoded(str)) str else URLEncoder.encode(str, "utf-8")

// TODO: this could be removed when the Uri class in akka-http will accept utf-8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able to find appropriate ticket in akka-http. Should one be created?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

akka/akka-http#86 - this is a similar issue, but for Cyrillic URIs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you add ticket number to comment into code?

Copy link
Author

@devsprint devsprint Dec 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. it will be in next push

whenReady(stsResult) { builtSts =>
withClue(s"According to the expected string to sign stored in ${sts._1}") {
if (sts._1 == "post-x-www-form-urlencoded.sts") {
// TODO: skip this test because we can't avoid adding charset into http request as the test suite require.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to consider this as http ticket?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probes.get.foreach {
case (fileName, expectedResult) =>
testCanonicalRequest(fileName, expectedResult)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 4 test cases are different only with testMethod and expected result - can this be a single function with results collection and test function as arguments?

* @return the Uri
*/
private def encodeIfRequired(rawUri: String): Uri = {
val pattern = """^([!#$&-;=?-\[\]_a-z~]|%[0-9a-fA-F]{2})+$$""".r
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need some explanation here, maybe comments

*/
private def encodeIfRequired(rawUri: String): Uri = {
val pattern = """^([!#$&-;=?-\[\]_a-z~]|%[0-9a-fA-F]{2})+$$""".r
// TODO: this look like a bug in Uri parser in akka-http ("//" is generating empty path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another akka-http ticket?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (current.contains(':')) {
current :: acc
} else {
// TODO: trim and ',' has been added to pass the test, but it looks like a bug in HttpParser code from akka-http.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

@agolubev
Copy link
Contributor

In general - looks good

@devsprint
Copy link
Author

@agolubev please take a look to last PR.

Copy link
Contributor

@agolubev agolubev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Thanks!

}

private def fixContentTypeHeaderParameter(headers: String): String =
headers.replace("charset=UTF-8", "charset=utf8")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break signing, as you sign a different request than what akka-http will send.

@jrudolph
Copy link
Member

I think this approach cannot work because not all of the test cases (the .req files) can be represented verbatim by the akka-http model. In the end, the point of the akka-http model is to remove many of the redundant representations by a single typed model which makes it harder to get many of the representation details wrong.

If you want to use the test cases, you need to check in the first step what the actually requests are that akka-http will send out and check these against the original test case. This will probably need to use HttpRequestRenderer or actual end-to-end testing by running the request through the complete Http.clientLayer to figure out what the actual request is. In many cases, it will not be the same as what the test cases need. The problem is then, that you cannot use the provided test data because you cannot make akka-http to send them unchanged, so you need to sign something else.

Worse, the changes to the signer might actually break signing because the canonical representation doesn't seem to be the same as what will be sent through akka-http, so that AWS will have to reject requests which were manipulated by the signer.

.replace(":", "%3A")
.replace("%2F", "/")
.replace("%7E", "~")
.replace("+", "%20")

def canonicalHeaderString(headers: Seq[HttpHeader]): String = {
val grouped = headers.groupBy(_.lowercaseName())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be missing the value of req.entity.contentType which is encoded specially in the akka-http model.

@devsprint
Copy link
Author

devsprint commented Dec 29, 2016

@jrudolph hmm, I understand your reasons and thanks for clarifications. Some parts of the test data from Amazon will not work with current constrains, but this is a small part of it, the ones that are having a body, where most of them are not having any. For this I will work out how to adapt it such that we can use it with smaller changes (.req, .creq.. changes). There are 2 test data sets that are failing (POST-x-www-form-urlencoded and POST-x-www-form-urlencoded-parameters). Are you ok with this approach ?

@patriknw
Copy link
Member

Not sure how important the comment is, do you know @ktoso ?
Can we merge and do a follow up?

@devsprint
Copy link
Author

@patriknw I will update the pull request based on @jrudolph comments but I will be only able to do it over the week-end.

@patriknw
Copy link
Member

Ok, thanks for the update, then I'll leave this open

/**
* Load the probes files from resource folder.
*/
object Probes {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probes is a bit of a weird name, in Akka probes are TestProbes hm.

@ktoso
Copy link
Member

ktoso commented Jan 13, 2017

Agree with @jrudolph's both contents. We must use the actual APIs to verify the test actually is testing what will be put onto the wire. Now it's a weird mix of hand constructing things which may be not the way we'd render these - please make the .req files into actual Akka HTTP model types directly. Where it's not possible we need to fix something or provide workarounds.

@patriknw
Copy link
Member

patriknw commented Feb 5, 2017

@devsprint any news here?

@jrudolph
Copy link
Member

Sorry for keeping this so long.

I think the bottom line here is:

  • it is impossible to implement the AWS test suite including the hashes given by the test suite because some of the test cases cannot be rendered by akka-http (that's by design, semantically equivalent headers can be rendered)
  • it is likely that this PR will break signing because the signer is changed in a way to make the tests pass (but the tests do not assert what akka-http renders)

Here's a sketch how to implement signing tests:

  • provide or generate a set of interesting test requests as akka-http HttpRequest (internal HttpRequestParser infrastructure could be used to parse the AWS test files, signatures of the test files cannot be used)
  • run each of the requests through akka-http's internal HttpRequestRenderer or use Http.clientLayer() flow (connect all ports of the BidiFlow to probes and then push a request and read the network side, to get access to the actual wire rendering of the request
  • use any third party library to sign this request data (which will involve parsing the request with whatever the third-party tool provides)
  • check that the alpakka AWS signer generates the same signature

Not sure if it makes sense to keep this PR open as the real solution might not have much in common with what we have here.

WDYT?

@devsprint
Copy link
Author

@jrudolph I have tried to find a way to still use the test cases from Amazon but as you also obeserved is it imposible to use them. I think that the right option is to close this PR.

@jrudolph
Copy link
Member

Ok. Let's close it for now. I'll add a comment in the ticket to see the comments here before tackling it again. Anyway, thanks for your work, @devsprint!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants