Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"variables": {
"${LATEST}": "3.330.0"
"${LATEST}": "3.330.1"
},
"endpoints": "https://raw.githubusercontent.com/aws/aws-sdk-php/${LATEST}/src/data/endpoints.json",
"services": {
Expand Down
4 changes: 4 additions & 0 deletions src/Service/S3/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## NOT RELEASED

### Added

- AWS api-change: Amazon Simple Storage Service / Features: Add support for ETag based conditional writes in PutObject and CompleteMultiPartUpload APIs to prevent unintended object modifications.

## 2.5.0

### Added
Expand Down
2 changes: 1 addition & 1 deletion src/Service/S3/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
}
}
38 changes: 38 additions & 0 deletions src/Service/S3/src/Input/CompleteMultipartUploadRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,26 @@ final class CompleteMultipartUploadRequest extends Input
*/
private $expectedBucketOwner;

/**
* Uploads the object only if the ETag (entity tag) value provided during the WRITE operation matches the ETag of the
* object in S3. If the ETag values do not match, the operation returns a `412 Precondition Failed` error.
*
* If a conflicting operation occurs during the upload S3 returns a `409 ConditionalRequestConflict` response. On a 409
* failure you should fetch the object's ETag, re-initiate the multipart upload with `CreateMultipartUpload`, and
* re-upload each part.
*
* Expects the ETag value as a string.
*
* For more information about conditional requests, see RFC 7232 [^1], or Conditional requests [^2] in the *Amazon S3
* User Guide*.
*
* [^1]: https://tools.ietf.org/html/rfc7232
* [^2]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-requests.html
*
* @var string|null
*/
private $ifMatch;

/**
* Uploads the object only if the object key name does not already exist in the bucket specified. Otherwise, Amazon S3
* returns a `412 Precondition Failed` error.
Expand Down Expand Up @@ -197,6 +217,7 @@ final class CompleteMultipartUploadRequest extends Input
* ChecksumSHA256?: null|string,
* RequestPayer?: null|RequestPayer::*,
* ExpectedBucketOwner?: null|string,
* IfMatch?: null|string,
* IfNoneMatch?: null|string,
* SSECustomerAlgorithm?: null|string,
* SSECustomerKey?: null|string,
Expand All @@ -216,6 +237,7 @@ public function __construct(array $input = [])
$this->checksumSha256 = $input['ChecksumSHA256'] ?? null;
$this->requestPayer = $input['RequestPayer'] ?? null;
$this->expectedBucketOwner = $input['ExpectedBucketOwner'] ?? null;
$this->ifMatch = $input['IfMatch'] ?? null;
$this->ifNoneMatch = $input['IfNoneMatch'] ?? null;
$this->sseCustomerAlgorithm = $input['SSECustomerAlgorithm'] ?? null;
$this->sseCustomerKey = $input['SSECustomerKey'] ?? null;
Expand All @@ -235,6 +257,7 @@ public function __construct(array $input = [])
* ChecksumSHA256?: null|string,
* RequestPayer?: null|RequestPayer::*,
* ExpectedBucketOwner?: null|string,
* IfMatch?: null|string,
* IfNoneMatch?: null|string,
* SSECustomerAlgorithm?: null|string,
* SSECustomerKey?: null|string,
Expand Down Expand Up @@ -277,6 +300,11 @@ public function getExpectedBucketOwner(): ?string
return $this->expectedBucketOwner;
}

public function getIfMatch(): ?string
{
return $this->ifMatch;
}

public function getIfNoneMatch(): ?string
{
return $this->ifNoneMatch;
Expand Down Expand Up @@ -348,6 +376,9 @@ public function request(): Request
if (null !== $this->expectedBucketOwner) {
$headers['x-amz-expected-bucket-owner'] = $this->expectedBucketOwner;
}
if (null !== $this->ifMatch) {
$headers['If-Match'] = $this->ifMatch;
}
if (null !== $this->ifNoneMatch) {
$headers['If-None-Match'] = $this->ifNoneMatch;
}
Expand Down Expand Up @@ -433,6 +464,13 @@ public function setExpectedBucketOwner(?string $value): self
return $this;
}

public function setIfMatch(?string $value): self
{
$this->ifMatch = $value;

return $this;
}

public function setIfNoneMatch(?string $value): self
{
$this->ifNoneMatch = $value;
Expand Down
37 changes: 37 additions & 0 deletions src/Service/S3/src/Input/PutObjectRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,25 @@ final class PutObjectRequest extends Input
*/
private $expires;

/**
* Uploads the object only if the ETag (entity tag) value provided during the WRITE operation matches the ETag of the
* object in S3. If the ETag values do not match, the operation returns a `412 Precondition Failed` error.
*
* If a conflicting operation occurs during the upload S3 returns a `409 ConditionalRequestConflict` response. On a 409
* failure you should fetch the object's ETag and retry the upload.
*
* Expects the ETag value as a string.
*
* For more information about conditional requests, see RFC 7232 [^1], or Conditional requests [^2] in the *Amazon S3
* User Guide*.
*
* [^1]: https://tools.ietf.org/html/rfc7232
* [^2]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-requests.html
*
* @var string|null
*/
private $ifMatch;

/**
* Uploads the object only if the object key name does not already exist in the bucket specified. Otherwise, Amazon S3
* returns a `412 Precondition Failed` error.
Expand Down Expand Up @@ -597,6 +616,7 @@ final class PutObjectRequest extends Input
* ChecksumSHA1?: null|string,
* ChecksumSHA256?: null|string,
* Expires?: null|\DateTimeImmutable|string,
* IfMatch?: null|string,
* IfNoneMatch?: null|string,
* GrantFullControl?: null|string,
* GrantRead?: null|string,
Expand Down Expand Up @@ -641,6 +661,7 @@ public function __construct(array $input = [])
$this->checksumSha1 = $input['ChecksumSHA1'] ?? null;
$this->checksumSha256 = $input['ChecksumSHA256'] ?? null;
$this->expires = !isset($input['Expires']) ? null : ($input['Expires'] instanceof \DateTimeImmutable ? $input['Expires'] : new \DateTimeImmutable($input['Expires']));
$this->ifMatch = $input['IfMatch'] ?? null;
$this->ifNoneMatch = $input['IfNoneMatch'] ?? null;
$this->grantFullControl = $input['GrantFullControl'] ?? null;
$this->grantRead = $input['GrantRead'] ?? null;
Expand Down Expand Up @@ -685,6 +706,7 @@ public function __construct(array $input = [])
* ChecksumSHA1?: null|string,
* ChecksumSHA256?: null|string,
* Expires?: null|\DateTimeImmutable|string,
* IfMatch?: null|string,
* IfNoneMatch?: null|string,
* GrantFullControl?: null|string,
* GrantRead?: null|string,
Expand Down Expand Up @@ -835,6 +857,11 @@ public function getGrantWriteAcp(): ?string
return $this->grantWriteAcp;
}

public function getIfMatch(): ?string
{
return $this->ifMatch;
}

public function getIfNoneMatch(): ?string
{
return $this->ifNoneMatch;
Expand Down Expand Up @@ -993,6 +1020,9 @@ public function request(): Request
if (null !== $this->expires) {
$headers['Expires'] = $this->expires->setTimezone(new \DateTimeZone('GMT'))->format(\DateTimeInterface::RFC7231);
}
if (null !== $this->ifMatch) {
$headers['If-Match'] = $this->ifMatch;
}
if (null !== $this->ifNoneMatch) {
$headers['If-None-Match'] = $this->ifNoneMatch;
}
Expand Down Expand Up @@ -1262,6 +1292,13 @@ public function setGrantWriteAcp(?string $value): self
return $this;
}

public function setIfMatch(?string $value): self
{
$this->ifMatch = $value;

return $this;
}

public function setIfNoneMatch(?string $value): self
{
$this->ifNoneMatch = $value;
Expand Down
2 changes: 2 additions & 0 deletions src/Service/S3/src/S3Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ public function bucketNotExists($input): BucketNotExistsWaiter
* ChecksumSHA256?: null|string,
* RequestPayer?: null|RequestPayer::*,
* ExpectedBucketOwner?: null|string,
* IfMatch?: null|string,
* IfNoneMatch?: null|string,
* SSECustomerAlgorithm?: null|string,
* SSECustomerKey?: null|string,
Expand Down Expand Up @@ -2467,6 +2468,7 @@ public function putBucketTagging($input): Result
* ChecksumSHA1?: null|string,
* ChecksumSHA256?: null|string,
* Expires?: null|\DateTimeImmutable|string,
* IfMatch?: null|string,
* IfNoneMatch?: null|string,
* GrantFullControl?: null|string,
* GrantRead?: null|string,
Expand Down