1 1 | /*
|
2 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3 3 | * SPDX-License-Identifier: Apache-2.0
|
4 4 | */
|
5 5 |
|
6 6 | //! Functions shared between the tests of several modules.
|
7 7 |
|
8 - | use crate::http_request::{SignableBody, SignableRequest};
|
8 + | use crate::http_request::canonical_request::{CanonicalRequest, StringToSign};
|
9 + | use crate::http_request::{
|
10 + | PayloadChecksumKind, SessionTokenMode, SignableBody, SignableRequest, SignatureLocation,
|
11 + | SigningSettings,
|
12 + | };
|
13 + | use aws_credential_types::Credentials;
|
14 + | use aws_smithy_runtime_api::client::identity::Identity;
|
9 15 | use http0::{Method, Uri};
|
16 + | use std::borrow::Cow;
|
10 17 | use std::error::Error as StdError;
|
18 + | use std::time::{Duration, SystemTime};
|
19 + | use time::format_description::well_known::Rfc3339;
|
20 + | use time::OffsetDateTime;
|
21 + |
|
22 + | /// Common test suite collection
|
23 + | #[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
24 + | enum Collection {
|
25 + | V4,
|
26 + | V4A,
|
27 + | }
|
11 28 |
|
12 - | pub(crate) mod v4 {
|
13 - | use super::*;
|
29 + | /// A test from the common CRT test suite
|
30 + | #[derive(Debug, Clone)]
|
31 + | pub(crate) struct SigningSuiteTest {
|
32 + | test_name: &'static str,
|
33 + | collection: Collection,
|
34 + | }
|
14 35 |
|
15 - | fn path(name: &str, ext: &str) -> String {
|
16 - | format!("aws-sig-v4-test-suite/{}/{}.{}", name, name, ext)
|
36 + | impl SigningSuiteTest {
|
37 + | /// Create a new test from the V4 test suite
|
38 + | pub(crate) fn v4(test_name: &'static str) -> Self {
|
39 + | Self {
|
40 + | test_name,
|
41 + | collection: Collection::V4,
|
42 + | }
|
17 43 | }
|
18 44 |
|
19 - | pub(crate) fn test_canonical_request(name: &str) -> String {
|
20 - | // Tests fail if there's a trailing newline in the file, and pre-commit requires trailing newlines
|
21 - | read(&path(name, "creq")).trim().to_string()
|
45 + | /// Create a new test from the V4a test suite
|
46 + | pub(crate) fn v4a(test_name: &'static str) -> Self {
|
47 + | Self {
|
48 + | test_name,
|
49 + | collection: Collection::V4A,
|
50 + | }
|
22 51 | }
|
23 52 |
|
24 - | pub(crate) fn test_sts(name: &str) -> String {
|
25 - | read(&path(name, "sts"))
|
53 + | /// Get the path to a file in this test suite directory
|
54 + | fn path(&self, filename: &str) -> String {
|
55 + | let dir = match self.collection {
|
56 + | Collection::V4 => "v4",
|
57 + | Collection::V4A => "v4a",
|
58 + | };
|
59 + | format!("aws-signing-test-suite/{dir}/{}/{filename}", self.test_name)
|
26 60 | }
|
27 61 |
|
28 - | pub(crate) fn test_request(name: &str) -> TestRequest {
|
29 - | test_parsed_request(name, "req")
|
62 + | /// Get the HTTP request for the test
|
63 + | pub(crate) fn request(&self) -> TestRequest {
|
64 + | test_parsed_request(&self.path("request.txt"))
|
30 65 | }
|
31 66 |
|
32 - | pub(crate) fn test_signed_request(name: &str) -> TestRequest {
|
33 - | test_parsed_request(name, "sreq")
|
67 + | /// Get the signed HTTP request for the test
|
68 + | pub(crate) fn signed_request(&self, signature_location: SignatureLocation) -> TestRequest {
|
69 + | match signature_location {
|
70 + | SignatureLocation::QueryParams => {
|
71 + | test_parsed_request(&self.path("query-signed-request.txt"))
|
72 + | }
|
73 + | SignatureLocation::Headers => {
|
74 + | test_parsed_request(&self.path("header-signed-request.txt"))
|
75 + | }
|
76 + | }
|
34 77 | }
|
35 78 |
|
36 - | pub(crate) fn test_signed_request_query_params(name: &str) -> TestRequest {
|
37 - | test_parsed_request(name, "qpsreq")
|
79 + | /// Get the canonical request for the test
|
80 + | pub(crate) fn canonical_request(&self, signature_location: SignatureLocation) -> String {
|
81 + | match signature_location {
|
82 + | SignatureLocation::QueryParams => read(&self.path("query-canonical-request.txt")),
|
83 + | SignatureLocation::Headers => read(&self.path("header-canonical-request.txt")),
|
84 + | }
|
38 85 | }
|
39 86 |
|
40 - | fn test_parsed_request(name: &str, ext: &str) -> TestRequest {
|
41 - | let path = path(name, ext);
|
42 - | match parse_request(read(&path).as_bytes()) {
|
43 - | Ok(parsed) => parsed,
|
44 - | Err(err) => panic!("Failed to parse {}: {}", path, err),
|
87 + | /// Get the string to sign for the test
|
88 + | pub(crate) fn string_to_sign(&self, signature_location: SignatureLocation) -> String {
|
89 + | match signature_location {
|
90 + | SignatureLocation::QueryParams => read(&self.path("query-string-to-sign.txt")),
|
91 + | SignatureLocation::Headers => read(&self.path("header-string-to-sign.txt")),
|
45 92 | }
|
46 93 | }
|
47 94 |
|
48 - | #[test]
|
49 - | fn test_parse() {
|
50 - | test_request("post-header-key-case");
|
95 + | /// Get the signature for the test
|
96 + | pub(crate) fn signature(&self, signature_location: SignatureLocation) -> String {
|
97 + | match signature_location {
|
98 + | SignatureLocation::QueryParams => read(&self.path("query-signature.txt")),
|
99 + | SignatureLocation::Headers => read(&self.path("header-signature.txt")),
|
100 + | }
|
51 101 | }
|
52 102 |
|
53 - | #[test]
|
54 - | fn test_read_query_params() {
|
55 - | test_request("get-vanilla-query-order-key-case");
|
103 + | /// Get the test context for the test
|
104 + | pub(crate) fn context(&self) -> TestContext {
|
105 + | let context = read(&self.path("context.json"));
|
106 + | let tc_builder: TestContextBuilder = serde_json::from_str(&context).unwrap();
|
107 + | tc_builder.build()
|
56 108 | }
|
57 109 | }
|
58 110 |
|
59 - | #[cfg(feature = "sigv4a")]
|
60 - | pub(crate) mod v4a {
|
61 - | use super::*;
|
62 - | use crate::http_request::{
|
63 - | PayloadChecksumKind, SessionTokenMode, SignatureLocation, SigningSettings,
|
64 - | };
|
65 - | use aws_credential_types::Credentials;
|
66 - | use aws_smithy_runtime_api::client::identity::Identity;
|
67 - | use serde_derive::Deserialize;
|
68 - | use std::time::{Duration, SystemTime};
|
69 - | use time::format_description::well_known::Rfc3339;
|
70 - | use time::OffsetDateTime;
|
71 - |
|
72 - | fn path(test_name: &str, definition_name: &str) -> String {
|
73 - | format!("aws-sig-v4a-test-suite/{test_name}/{definition_name}.txt")
|
111 + | fn test_parsed_request(path: &str) -> TestRequest {
|
112 + | match parse_request(read(path).as_bytes()) {
|
113 + | Ok(parsed) => parsed,
|
114 + | Err(err) => panic!("Failed to parse {}: {}", path, err),
|
74 115 | }
|
116 + | }
|
75 117 |
|
76 - | pub(crate) fn test_request(name: &str) -> TestRequest {
|
77 - | test_parsed_request(&path(name, "request"))
|
78 - | }
|
118 + | fn new_v4_signing_params_from_context(
|
119 + | test_context: &'_ TestContext,
|
120 + | signature_location: SignatureLocation,
|
121 + | ) -> crate::http_request::SigningParams<'_> {
|
122 + | let mut params = crate::sign::v4::SigningParams::from(test_context);
|
123 + | params.settings.signature_location = signature_location;
|
124 + | params.into()
|
125 + | }
|
79 126 |
|
80 - | pub(crate) fn test_canonical_request(
|
81 - | name: &str,
|
82 - | signature_location: SignatureLocation,
|
83 - | ) -> String {
|
84 - | match signature_location {
|
85 - | SignatureLocation::QueryParams => read(&path(name, "query-canonical-request")),
|
86 - | SignatureLocation::Headers => read(&path(name, "header-canonical-request")),
|
127 + | /// Run the given test from the v4 suite for both header and query
|
128 + | /// signature locations
|
129 + | pub(crate) fn run_test_suite_v4(test_name: &'static str) {
|
130 + | run_v4_test(test_name, SignatureLocation::Headers);
|
131 + | run_v4_test(test_name, SignatureLocation::QueryParams);
|
132 + | }
|
133 + |
|
134 + | fn assert_uri_eq(expected: &Uri, actual: &Uri) {
|
135 + | assert_eq!(expected.scheme(), actual.scheme());
|
136 + | assert_eq!(expected.authority(), actual.authority());
|
137 + | assert_eq!(expected.path(), actual.path());
|
138 + |
|
139 + | // query params may be out of order
|
140 + | let mut expected_params: Vec<(Cow<'_, str>, Cow<'_, str>)> =
|
141 + | form_urlencoded::parse(expected.query().unwrap_or_default().as_bytes()).collect();
|
142 + | expected_params.sort();
|
143 + |
|
144 + | let mut actual_params: Vec<(Cow<'_, str>, Cow<'_, str>)> =
|
145 + | form_urlencoded::parse(actual.query().unwrap_or_default().as_bytes()).collect();
|
146 + | actual_params.sort();
|
147 + |
|
148 + | assert_eq!(expected_params, actual_params);
|
149 + | }
|
150 + |
|
151 + | fn assert_requests_eq(expected: TestRequest, actual: http0::Request<&str>) {
|
152 + | let expected = expected.as_http_request();
|
153 + | let actual = actual;
|
154 + | assert_eq!(expected.method(), actual.method());
|
155 + | assert_eq!(
|
156 + | expected.headers().len(),
|
157 + | actual.headers().len(),
|
158 + | "extra or missing headers"
|
159 + | );
|
160 + | assert_eq!(expected.headers(), actual.headers(), "headers mismatch");
|
161 + | assert_uri_eq(expected.uri(), actual.uri());
|
162 + | assert_eq!(*expected.body(), *actual.body(), "body mismatch");
|
163 + | }
|
164 + |
|
165 + | /// Run the given test from the v4 suite for the given signature location
|
166 + | pub(crate) fn run_v4_test(test_name: &'static str, signature_location: SignatureLocation) {
|
167 + | let test = SigningSuiteTest::v4(test_name);
|
168 + | let tc = test.context();
|
169 + | let params = new_v4_signing_params_from_context(&tc, signature_location);
|
170 + |
|
171 + | let req = test.request();
|
172 + | let expected_creq = test.canonical_request(signature_location);
|
173 + | let signable_req = SignableRequest::from(&req);
|
174 + | let actual_creq = CanonicalRequest::from(&signable_req, ¶ms).unwrap();
|
175 + |
|
176 + | // check canonical request
|
177 + | assert_eq!(
|
178 + | expected_creq,
|
179 + | actual_creq.to_string(),
|
180 + | "canonical request didn't match (signature location: {signature_location:?})"
|
181 + | );
|
182 + |
|
183 + | let expected_string_to_sign = test.string_to_sign(signature_location);
|
184 + | let hashed_creq = &crate::sign::v4::sha256_hex_string(actual_creq.to_string().as_bytes());
|
185 + | let actual_string_to_sign = StringToSign::new_v4(
|
186 + | *params.time(),
|
187 + | params.region().unwrap(),
|
188 + | params.name(),
|
189 + | hashed_creq,
|
190 + | )
|
191 + | .to_string();
|
192 + |
|
193 + | // check string to sign
|
194 + | assert_eq!(
|
195 + | expected_string_to_sign, actual_string_to_sign,
|
196 + | "'string to sign' didn't match (signature location: {signature_location:?})"
|
197 + | );
|
198 + |
|
199 + | let out = crate::http_request::sign(signable_req, ¶ms).unwrap();
|
200 + | let mut signed = req.as_http_request();
|
201 + | out.output.apply_to_request_http0x(&mut signed);
|
202 + |
|
203 + | // check signature
|
204 + | assert_eq!(
|
205 + | test.signature(signature_location),
|
206 + | out.signature,
|
207 + | "signature didn't match (signature location: {signature_location:?})"
|
208 + | );
|
209 + |
|
210 + | let expected = test.signed_request(signature_location);
|
211 + | assert_requests_eq(expected, signed);
|
212 + | }
|
213 + |
|
214 + | /// Test suite context.json
|
215 + | pub(crate) struct TestContext {
|
216 + | pub(crate) identity: Identity,
|
217 + | pub(crate) expiration_in_seconds: u64,
|
218 + | pub(crate) normalize: bool,
|
219 + | pub(crate) region: String,
|
220 + | pub(crate) service: String,
|
221 + | pub(crate) timestamp: String,
|
222 + | pub(crate) omit_session_token: bool,
|
223 + | pub(crate) sign_body: bool,
|
224 + | }
|
225 + |
|
226 + | // Serde has limitations requiring this odd workaround.
|
227 + | // See https://github.com/serde-rs/serde/issues/368 for more info.
|
228 + | fn return_true() -> bool {
|
229 + | true
|
230 + | }
|
231 + |
|
232 + | #[derive(serde_derive::Deserialize)]
|
233 + | pub(crate) struct TestContextBuilder {
|
234 + | credentials: TestContextCreds,
|
235 + | expiration_in_seconds: u64,
|
236 + | normalize: bool,
|
237 + | region: String,
|
238 + | service: String,
|
239 + | timestamp: String,
|
240 + | #[serde(default)]
|
241 + | omit_session_token: bool,
|
242 + | #[serde(default = "return_true")]
|
243 + | sign_body: bool,
|
244 + | }
|
245 + |
|
246 + | impl TestContextBuilder {
|
247 + | pub(crate) fn build(self) -> TestContext {
|
248 + | let identity = Identity::new(
|
249 + | Credentials::from_keys(
|
250 + | &self.credentials.access_key_id,
|
251 + | &self.credentials.secret_access_key,
|
252 + | self.credentials.token.clone(),
|
253 + | ),
|
254 + | Some(SystemTime::UNIX_EPOCH + Duration::from_secs(self.expiration_in_seconds)),
|
255 + | );
|
256 + |
|
257 + | TestContext {
|
258 + | identity,
|
259 + | expiration_in_seconds: self.expiration_in_seconds,
|
260 + | normalize: self.normalize,
|
261 + | region: self.region,
|
262 + | service: self.service,
|
263 + | timestamp: self.timestamp,
|
264 + | omit_session_token: self.omit_session_token,
|
265 + | sign_body: self.sign_body,
|
87 266 | }
|
88 267 | }
|
268 + | }
|
89 269 |
|
90 - | pub(crate) fn test_string_to_sign(name: &str, signature_location: SignatureLocation) -> String {
|
91 - | match signature_location {
|
92 - | SignatureLocation::QueryParams => read(&path(name, "query-string-to-sign")),
|
93 - | SignatureLocation::Headers => read(&path(name, "header-string-to-sign")),
|
270 + | #[derive(serde_derive::Deserialize)]
|
271 + | pub(crate) struct TestContextCreds {
|
272 + | access_key_id: String,
|
273 + | secret_access_key: String,
|
274 + | token: Option<String>,
|
275 + | }
|
276 + |
|
277 + | impl<'a> From<&'a TestContext> for crate::sign::v4::SigningParams<'a, SigningSettings> {
|
278 + | fn from(tc: &'a TestContext) -> Self {
|
279 + | crate::sign::v4::SigningParams {
|
280 + | identity: &tc.identity,
|
281 + | region: &tc.region,
|
282 + | name: &tc.service,
|
283 + | time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
|
284 + | .unwrap()
|
285 + | .into(),
|
286 + | settings: SigningSettings {
|
287 + | // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
|
288 + | expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
|
289 + | uri_path_normalization_mode: tc.normalize.into(),
|
290 + | session_token_mode: if tc.omit_session_token {
|
291 + | SessionTokenMode::Exclude
|
292 + | } else {
|
293 + | SessionTokenMode::Include
|
294 + | },
|
295 + | payload_checksum_kind: if tc.sign_body {
|
296 + | PayloadChecksumKind::XAmzSha256
|
297 + | } else {
|
298 + | PayloadChecksumKind::NoHeader
|
299 + | },
|
300 + | ..Default::default()
|
301 + | },
|
94 302 | }
|
95 303 | }
|
304 + | }
|
96 305 |
|
97 - | fn test_parsed_request(path: &str) -> TestRequest {
|
98 - | match parse_request(read(path).as_bytes()) {
|
99 - | Ok(parsed) => parsed,
|
100 - | Err(err) => panic!("Failed to parse {}: {}", path, err),
|
101 - | }
|
306 + | #[cfg(feature = "sigv4a")]
|
307 + | pub(crate) mod v4a {
|
308 + | use super::*;
|
309 + | use crate::http_request::{
|
310 + | sign, PayloadChecksumKind, SessionTokenMode, SignatureLocation, SigningSettings,
|
311 + | };
|
312 + | use crate::sign::v4a;
|
313 + | use p256::ecdsa::signature::{Signature, Verifier};
|
314 + | use p256::ecdsa::{DerSignature, SigningKey};
|
315 + | use std::time::Duration;
|
316 + | use time::format_description::well_known::Rfc3339;
|
317 + | use time::OffsetDateTime;
|
318 + |
|
319 + | fn new_v4a_signing_params_from_context(
|
320 + | test_context: &'_ TestContext,
|
321 + | signature_location: SignatureLocation,
|
322 + | ) -> crate::http_request::SigningParams<'_> {
|
323 + | let mut params = crate::sign::v4a::SigningParams::from(test_context);
|
324 + | params.settings.signature_location = signature_location;
|
325 + | params.into()
|
102 326 | }
|
103 327 |
|
104 - | pub(crate) fn test_context(test_name: &str) -> TestContext {
|
105 - | let path = format!("aws-sig-v4a-test-suite/{test_name}/context.json");
|
106 - | let context = read(&path);
|
107 - | let tc_builder: TestContextBuilder = serde_json::from_str(&context).unwrap();
|
108 - | tc_builder.build()
|
328 + | pub(crate) fn run_test_suite_v4a(test_name: &'static str) {
|
329 + | run_v4a_test(test_name, SignatureLocation::Headers);
|
330 + | run_v4a_test(test_name, SignatureLocation::QueryParams);
|
109 331 | }
|
110 332 |
|
111 - | pub(crate) struct TestContext {
|
112 - | pub(crate) identity: Identity,
|
113 - | pub(crate) expiration_in_seconds: u64,
|
114 - | pub(crate) normalize: bool,
|
115 - | pub(crate) region: String,
|
116 - | pub(crate) service: String,
|
117 - | pub(crate) timestamp: String,
|
118 - | pub(crate) omit_session_token: bool,
|
119 - | pub(crate) sign_body: bool,
|
333 + | pub(crate) fn run_v4a_test(test_name: &'static str, signature_location: SignatureLocation) {
|
334 + | let test = SigningSuiteTest::v4a(test_name);
|
335 + | let tc = test.context();
|
336 + | let params = new_v4a_signing_params_from_context(&tc, signature_location);
|
337 + |
|
338 + | let req = test.request();
|
339 + | let expected_creq = test.canonical_request(signature_location);
|
340 + | let signable_req = SignableRequest::from(&req);
|
341 + | let actual_creq = CanonicalRequest::from(&signable_req, ¶ms).unwrap();
|
342 + |
|
343 + | assert_eq!(
|
344 + | expected_creq,
|
345 + | actual_creq.to_string(),
|
346 + | "canonical request didn't match (signature location: {signature_location:?})"
|
347 + | );
|
348 + |
|
349 + | let expected_string_to_sign = test.string_to_sign(signature_location);
|
350 + | let hashed_creq = &crate::sign::v4::sha256_hex_string(actual_creq.to_string().as_bytes());
|
351 + | let actual_string_to_sign = StringToSign::new_v4a(
|
352 + | *params.time(),
|
353 + | params.region_set().unwrap(),
|
354 + | params.name(),
|
355 + | hashed_creq,
|
356 + | )
|
357 + | .to_string();
|
358 + |
|
359 + | assert_eq!(
|
360 + | expected_string_to_sign, actual_string_to_sign,
|
361 + | "'string to sign' didn't match (signature location: {signature_location:?})"
|
362 + | );
|
363 + |
|
364 + | let out = sign(signable_req, ¶ms).unwrap();
|
365 + | // Sigv4a signatures are non-deterministic, so we can't compare the signature directly.
|
366 + | out.output
|
367 + | .apply_to_request_http0x(&mut req.as_http_request());
|
368 + |
|
369 + | let creds = params.credentials().unwrap();
|
370 + | let signing_key =
|
371 + | v4a::generate_signing_key(creds.access_key_id(), creds.secret_access_key());
|
372 + | let sig = DerSignature::from_bytes(&hex::decode(out.signature).unwrap()).unwrap();
|
373 + | let sig = sig
|
374 + | .try_into()
|
375 + | .expect("DER-style signatures are always convertible into fixed-size signatures");
|
376 + |
|
377 + | let signing_key = SigningKey::from_bytes(signing_key.as_ref()).unwrap();
|
378 + | let peer_public_key = signing_key.verifying_key();
|
379 + | let sts = actual_string_to_sign.as_bytes();
|
380 + | peer_public_key.verify(sts, &sig).unwrap();
|
381 + | // TODO(sigv4a) - use public.key.json as verifying key?
|
120 382 | }
|
121 383 |
|
122 384 | impl<'a> From<&'a TestContext> for crate::sign::v4a::SigningParams<'a, SigningSettings> {
|
123 385 | fn from(tc: &'a TestContext) -> Self {
|
124 386 | crate::sign::v4a::SigningParams {
|
125 387 | identity: &tc.identity,
|
126 388 | region_set: &tc.region,
|
127 389 | name: &tc.service,
|
128 390 | time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
|
129 391 | .unwrap()
|
130 392 | .into(),
|
131 393 | settings: SigningSettings {
|
132 394 | // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
|
133 395 | expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
|
134 396 | uri_path_normalization_mode: tc.normalize.into(),
|
135 397 | session_token_mode: if tc.omit_session_token {
|
136 398 | SessionTokenMode::Exclude
|
137 399 | } else {
|
138 400 | SessionTokenMode::Include
|
139 401 | },
|
140 402 | payload_checksum_kind: if tc.sign_body {
|
141 403 | PayloadChecksumKind::XAmzSha256
|
142 404 | } else {
|
143 405 | PayloadChecksumKind::NoHeader
|
144 406 | },
|
145 407 | ..Default::default()
|
146 408 | },
|
147 409 | }
|
148 410 | }
|
149 411 | }
|
150 412 |
|
151 - | // Serde has limitations requiring this odd workaround.
|
152 - | // See https://github.com/serde-rs/serde/issues/368 for more info.
|
153 - | fn return_true() -> bool {
|
154 - | true
|
155 - | }
|
156 - |
|
157 - | #[derive(Deserialize)]
|
158 - | pub(crate) struct TestContextBuilder {
|
159 - | credentials: TestContextCreds,
|
160 - | expiration_in_seconds: u64,
|
161 - | normalize: bool,
|
162 - | region: String,
|
163 - | service: String,
|
164 - | timestamp: String,
|
165 - | #[serde(default)]
|
166 - | omit_session_token: bool,
|
167 - | #[serde(default = "return_true")]
|
168 - | sign_body: bool,
|
169 - | }
|
170 - |
|
171 - | impl TestContextBuilder {
|
172 - | pub(crate) fn build(self) -> TestContext {
|
173 - | let identity = Identity::new(
|
174 - | Credentials::from_keys(
|
175 - | &self.credentials.access_key_id,
|
176 - | &self.credentials.secret_access_key,
|
177 - | self.credentials.token.clone(),
|
178 - | ),
|
179 - | Some(SystemTime::UNIX_EPOCH + Duration::from_secs(self.expiration_in_seconds)),
|
180 - | );
|
181 - |
|
182 - | TestContext {
|
183 - | identity,
|
184 - | expiration_in_seconds: self.expiration_in_seconds,
|
185 - | normalize: self.normalize,
|
186 - | region: self.region,
|
187 - | service: self.service,
|
188 - | timestamp: self.timestamp,
|
189 - | omit_session_token: self.omit_session_token,
|
190 - | sign_body: self.sign_body,
|
191 - | }
|
192 - | }
|
193 - | }
|
194 - |
|
195 - | #[derive(Deserialize)]
|
196 - | pub(crate) struct TestContextCreds {
|
197 - | access_key_id: String,
|
198 - | secret_access_key: String,
|
199 - | token: Option<String>,
|
200 - | }
|
201 - |
|
202 413 | #[test]
|
203 414 | fn test_parse() {
|
204 - | let req = test_request("post-header-key-case");
|
415 + | let req = SigningSuiteTest::v4a("post-header-key-case").request();
|
205 416 | assert_eq!(req.method, "POST");
|
206 417 | assert_eq!(req.uri, "https://example.amazonaws.com/");
|
207 418 | assert!(req.headers.is_empty());
|
208 419 | }
|
209 420 |
|
210 421 | #[test]
|
211 422 | fn test_read_query_params() {
|
212 - | let req = test_request("get-header-value-trim");
|
423 + | let req = SigningSuiteTest::v4a("get-header-value-trim").request();
|
213 424 | assert_eq!(req.method, "GET");
|
214 425 | assert_eq!(req.uri, "https://example.amazonaws.com/");
|
215 426 | assert!(!req.headers.is_empty());
|
216 427 | }
|
217 428 | }
|
218 429 |
|
219 430 | fn read(path: &str) -> String {
|
220 431 | println!("Loading `{}` for test case...", path);
|
221 432 | let v = {
|
222 433 | match std::fs::read_to_string(path) {
|