Skip to content

Commit 96deb3c

Browse files
committed
Merge remote-tracking branch 'upstream/main' into jdisanti-configure-cache-default-provider
2 parents ab85893 + b989a8d commit 96deb3c

File tree

15 files changed

+194
-144
lines changed

15 files changed

+194
-144
lines changed

.github/workflows/ci-sdk.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
- name: Clippy
6969
run: cargo clippy --all-features
7070
- name: Unused Dependencies
71-
run: cargo +nightly udeps
71+
run: cargo +nightly-2022-02-22 udeps
7272
- name: Additional per-crate checks
7373
run: ../tools/additional-per-crate-checks.sh ./sdk/ ../tools/ci-cdk/
7474
env:
@@ -87,7 +87,7 @@ jobs:
8787
default: true
8888
- uses: actions-rs/toolchain@v1
8989
with:
90-
toolchain: nightly
90+
toolchain: nightly-2022-02-22
9191
default: false
9292
- name: Cache cargo bin
9393
uses: actions/cache@v2
@@ -97,7 +97,7 @@ jobs:
9797
- name: Install additional cargo binaries
9898
run: |
9999
if [[ ! -f ~/.cargo/bin/cargo-udeps ]]; then
100-
cargo +nightly install cargo-udeps
100+
cargo +nightly-2022-02-22 install cargo-udeps
101101
fi
102102
if [[ ! -f ~/.cargo/bin/cargo-hack ]]; then
103103
cargo install cargo-hack

CHANGELOG.next.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ references = ["smithy-rs#1217", "aws-sdk-rust#467"]
4747
meta = { "breaking" = false, "tada" = false, "bug" = true }
4848
author = "jdisanti"
4949

50+
[[aws-sdk-rust]]
51+
message = "`aws-sigv4` no longer skips the `content-length` and `content-type` headers when signing with `SignatureLocation::QueryParams`"
52+
references = ["smithy-rs#1216"]
53+
meta = { "breaking" = true, "tada" = false, "bug" = false }
54+
author = "jdisanti"
55+
56+
[[aws-sdk-rust]]
57+
message = "Fixed a bug in S3 that prevented the `content-length` and `content-type` inputs from being included in a presigned request signature. With this fix, customers can generate presigned URLs that enforce `content-length` and `content-type` for requests to S3."
58+
references = ["smithy-rs#1216", "aws-sdk-rust#466"]
59+
meta = { "breaking" = false, "tada" = false, "bug" = true }
60+
author = "jdisanti"
61+
5062
[[aws-sdk-rust]]
5163
message = "Made it possible to change settings, such as load timeout, on the credential cache used by the `DefaultCredentialsChain`."
5264
references = ["smithy-rs#1220", "aws-sdk-rust#462"]

aws/rust-runtime/aws-inlineable/src/presigning.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ pub mod request {
219219
pub(crate) mod service {
220220
use crate::presigning::request::PresignedRequest;
221221
use aws_smithy_http::operation;
222-
use http::header::{CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT};
222+
use http::header::USER_AGENT;
223223
use std::future::{ready, Ready};
224224
use std::marker::PhantomData;
225225
use std::task::{Context, Poll};
@@ -262,12 +262,6 @@ pub(crate) mod service {
262262
fn call(&mut self, req: operation::Request) -> Self::Future {
263263
let (mut req, _) = req.into_parts();
264264

265-
// Remove headers from input serialization that shouldn't be part of the presigned
266-
// request since the request body is unsigned and left up to the person making the final
267-
// HTTP request.
268-
req.headers_mut().remove(CONTENT_LENGTH);
269-
req.headers_mut().remove(CONTENT_TYPE);
270-
271265
// Remove user agent headers since the request will not be executed by the AWS Rust SDK.
272266
req.headers_mut().remove(USER_AGENT);
273267
req.headers_mut().remove("X-Amz-User-Agent");

aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::http_request::sign::SignableRequest;
1010
use crate::http_request::url_escape::percent_encode_path;
1111
use crate::http_request::PercentEncodingMode;
1212
use crate::sign::sha256_hex_string;
13-
use http::header::{HeaderName, CONTENT_LENGTH, CONTENT_TYPE, HOST, USER_AGENT};
13+
use http::header::{HeaderName, HOST, USER_AGENT};
1414
use http::{HeaderMap, HeaderValue, Method, Uri};
1515
use std::borrow::Cow;
1616
use std::cmp::Ordering;
@@ -223,11 +223,6 @@ impl<'a> CanonicalRequest<'a> {
223223
continue;
224224
}
225225
if params.settings.signature_location == SignatureLocation::QueryParams {
226-
// Exclude content-length and content-type for query param signatures since the
227-
// body is unsigned for these use-cases, and the size is not known up-front.
228-
if name == CONTENT_LENGTH || name == CONTENT_TYPE {
229-
continue;
230-
}
231226
// The X-Amz-User-Agent header should not be signed if this is for a presigned URL
232227
if name == HeaderName::from_static(header::X_AMZ_USER_AGENT) {
233228
continue;
@@ -675,7 +670,7 @@ mod tests {
675670
assert_eq!(expected, actual);
676671
}
677672

678-
// It should exclude user-agent, content-type, content-length, and x-amz-user-agent headers from presigning
673+
// It should exclude user-agent and x-amz-user-agent headers from presigning
679674
#[test]
680675
fn presigning_header_exclusion() {
681676
let request = http::Request::builder()
@@ -688,15 +683,20 @@ mod tests {
688683
.unwrap();
689684
let request = SignableRequest::from(&request);
690685

691-
let mut settings = SigningSettings::default();
692-
settings.signature_location = SignatureLocation::QueryParams;
693-
settings.expires_in = Some(Duration::from_secs(30));
686+
let settings = SigningSettings {
687+
signature_location: SignatureLocation::QueryParams,
688+
expires_in: Some(Duration::from_secs(30)),
689+
..Default::default()
690+
};
694691

695692
let signing_params = signing_params(settings);
696693
let canonical = CanonicalRequest::from(&request, &signing_params).unwrap();
697694

698695
let values = canonical.values.into_query_params().unwrap();
699-
assert_eq!("host", values.signed_headers.as_str());
696+
assert_eq!(
697+
"content-length;content-type;host",
698+
values.signed_headers.as_str()
699+
);
700700
}
701701

702702
#[test]

aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,22 +154,23 @@ class AwsInputPresignedMethod(
154154
val operationError = operationShape.errorSymbol(symbolProvider)
155155
val presignableOp = PRESIGNABLE_OPERATIONS.getValue(operationShape.id)
156156

157-
var makeOperationFn = "make_operation"
158-
if (presignableOp.hasModelTransforms()) {
159-
makeOperationFn = "_make_presigned_operation"
160-
161-
val syntheticOp =
162-
codegenContext.model.expectShape(syntheticShapeId(operationShape.id), OperationShape::class.java)
163-
val protocol = section.protocol
164-
MakeOperationGenerator(
165-
codegenContext,
166-
protocol,
167-
HttpBoundProtocolPayloadGenerator(codegenContext, protocol),
168-
// Prefixed with underscore to avoid colliding with modeled functions
169-
functionName = makeOperationFn,
170-
public = false,
171-
).generateMakeOperation(this, syntheticOp, section.customizations)
157+
val makeOperationOp = if (presignableOp.hasModelTransforms()) {
158+
codegenContext.model.expectShape(syntheticShapeId(operationShape.id), OperationShape::class.java)
159+
} else {
160+
section.operationShape
172161
}
162+
val makeOperationFn = "_make_presigned_operation"
163+
164+
val protocol = section.protocol
165+
MakeOperationGenerator(
166+
codegenContext,
167+
protocol,
168+
HttpBoundProtocolPayloadGenerator(codegenContext, protocol),
169+
// Prefixed with underscore to avoid colliding with modeled functions
170+
functionName = makeOperationFn,
171+
public = false,
172+
includeDefaultPayloadHeaders = false
173+
).generateMakeOperation(this, makeOperationOp, section.customizations)
173174

174175
documentPresignedMethod(hasConfigArg = true)
175176
rustBlockTemplate(

aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,7 @@ class SigV4SigningFeature(
157157
return when (section) {
158158
is OperationSection.MutateRequest -> writable {
159159
rustTemplate(
160-
"""
161-
##[allow(unused_mut)]
162-
let mut signing_config = #{sig_auth}::signer::OperationSigningConfig::default_config();
163-
""",
160+
"let mut signing_config = #{sig_auth}::signer::OperationSigningConfig::default_config();",
164161
*codegenScope
165162
)
166163
if (needsAmzSha256(service)) {

aws/sdk/integration-tests/s3/tests/presigning.rs

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,49 @@
44
*/
55

66
use aws_sdk_s3 as s3;
7+
use aws_sdk_s3::presigning::request::PresignedRequest;
8+
use http::header::{CONTENT_LENGTH, CONTENT_TYPE};
9+
use http::{HeaderMap, HeaderValue};
710
use s3::presigning::config::PresigningConfig;
811
use std::error::Error;
912
use std::time::{Duration, SystemTime};
1013

14+
/// Generates a `PresignedRequest` from the given input.
15+
/// Assumes that that input has a `presigned` method on it.
16+
macro_rules! presign_input {
17+
($input:expr) => {{
18+
let creds = s3::Credentials::new(
19+
"ANOTREAL",
20+
"notrealrnrELgWzOk3IfjzDKtFBhDby",
21+
Some("notarealsessiontoken".to_string()),
22+
None,
23+
"test",
24+
);
25+
let config = s3::Config::builder()
26+
.credentials_provider(creds)
27+
.region(s3::Region::new("us-east-1"))
28+
.build();
29+
30+
let req: PresignedRequest = $input
31+
.presigned(
32+
&config,
33+
PresigningConfig::builder()
34+
.start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891))
35+
.expires_in(Duration::from_secs(30))
36+
.build()
37+
.unwrap(),
38+
)
39+
.await?;
40+
req
41+
}};
42+
}
43+
1144
#[tokio::test]
1245
async fn test_presigning() -> Result<(), Box<dyn Error>> {
13-
let creds = s3::Credentials::new(
14-
"ANOTREAL",
15-
"notrealrnrELgWzOk3IfjzDKtFBhDby",
16-
Some("notarealsessiontoken".to_string()),
17-
None,
18-
"test",
19-
);
20-
let config = s3::Config::builder()
21-
.credentials_provider(creds)
22-
.region(s3::Region::new("us-east-1"))
23-
.build();
24-
25-
let input = s3::input::GetObjectInput::builder()
46+
let presigned = presign_input!(s3::input::GetObjectInput::builder()
2647
.bucket("test-bucket")
2748
.key("test-key")
28-
.build()?;
29-
30-
let presigned = input
31-
.presigned(
32-
&config,
33-
PresigningConfig::builder()
34-
.start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891))
35-
.expires_in(Duration::from_secs(30))
36-
.build()
37-
.unwrap(),
38-
)
39-
.await?;
49+
.build()?);
4050

4151
let pq = presigned.uri().path_and_query().unwrap();
4252
let path = pq.path();
@@ -63,3 +73,42 @@ async fn test_presigning() -> Result<(), Box<dyn Error>> {
6373

6474
Ok(())
6575
}
76+
77+
#[tokio::test]
78+
async fn test_presigning_with_payload_headers() -> Result<(), Box<dyn Error>> {
79+
let presigned = presign_input!(s3::input::PutObjectInput::builder()
80+
.bucket("test-bucket")
81+
.key("test-key")
82+
.content_length(12345)
83+
.content_type("application/x-test")
84+
.build()?);
85+
86+
let pq = presigned.uri().path_and_query().unwrap();
87+
let path = pq.path();
88+
let query = pq.query().unwrap();
89+
let mut query_params: Vec<&str> = query.split('&').collect();
90+
query_params.sort();
91+
92+
assert_eq!("PUT", presigned.method().as_str());
93+
assert_eq!("/test-bucket/test-key", path);
94+
assert_eq!(
95+
&[
96+
"X-Amz-Algorithm=AWS4-HMAC-SHA256",
97+
"X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request",
98+
"X-Amz-Date=20090213T233131Z",
99+
"X-Amz-Expires=30",
100+
"X-Amz-Security-Token=notarealsessiontoken",
101+
"X-Amz-Signature=6a22b8bf422d17fe25e7d9fcbd26df31397ca5e3ad07d1cec95326ffdbe4a0a2",
102+
"X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost",
103+
"x-id=PutObject"
104+
][..],
105+
&query_params
106+
);
107+
108+
let mut expected_headers = HeaderMap::new();
109+
expected_headers.insert(CONTENT_LENGTH, HeaderValue::from_static("12345"));
110+
expected_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/x-test"));
111+
assert_eq!(&expected_headers, presigned.headers());
112+
113+
Ok(())
114+
}

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ class ServerHttpProtocolGenerator(
7676
) : ProtocolGenerator(
7777
codegenContext,
7878
protocol,
79-
MakeOperationGenerator(codegenContext, protocol, HttpBoundProtocolPayloadGenerator(codegenContext, protocol)),
79+
MakeOperationGenerator(
80+
codegenContext,
81+
protocol,
82+
HttpBoundProtocolPayloadGenerator(codegenContext, protocol),
83+
public = true,
84+
includeDefaultPayloadHeaders = true
85+
),
8086
ServerHttpProtocolImplGenerator(codegenContext, protocol),
8187
) {
8288
// Define suffixes for operation input / output / error wrappers
@@ -468,7 +474,7 @@ private class ServerHttpProtocolImplGenerator(
468474
""",
469475
*codegenScope,
470476
)
471-
} ?:run {
477+
} ?: run {
472478
val payloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol, httpMessageType = HttpMessageType.RESPONSE)
473479
withBlockTemplate("let body = #{SmithyHttpServer}::body::to_boxed(", ");", *codegenScope) {
474480
payloadGenerator.generatePayload(this, "output", operationShape)

0 commit comments

Comments
 (0)