Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit 9b3cbf3

Browse filesBrowse files
authored
fix: fix rewrite object in CMEK enabled bucket (#807)
1 parent 4dd0907 commit 9b3cbf3
Copy full SHA for 9b3cbf3

3 files changed

+72-1Lines changed: 72 additions & 1 deletion

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎google/cloud/storage/blob.py‎

Copy file name to clipboardExpand all lines: google/cloud/storage/blob.py
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3576,7 +3576,15 @@ def rewrite(
35763576
if source.generation:
35773577
query_params["sourceGeneration"] = source.generation
35783578

3579-
if self.kms_key_name is not None:
3579+
# When a Customer Managed Encryption Key is used to encrypt Cloud Storage object
3580+
# at rest, object resource metadata will store the version of the Key Management
3581+
# Service cryptographic material. If a Blob instance with KMS Key metadata set is
3582+
# used to rewrite the object, then the existing kmsKeyName version
3583+
# value can't be used in the rewrite request and the client instead ignores it.
3584+
if (
3585+
self.kms_key_name is not None
3586+
and "cryptoKeyVersions" not in self.kms_key_name
3587+
):
35803588
query_params["destinationKmsKeyName"] = self.kms_key_name
35813589

35823590
_add_generation_match_parameters(
Collapse file

‎tests/system/test_kms_integration.py‎

Copy file name to clipboardExpand all lines: tests/system/test_kms_integration.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,17 @@ def test_blob_rewrite_rotate_csek_to_cmek(
224224

225225
assert dest.download_as_bytes() == source_data
226226

227+
# Test existing kmsKeyName version is ignored in the rewrite request
228+
dest = kms_bucket.get_blob(blob_name)
229+
source = kms_bucket.get_blob(blob_name)
230+
token, rewritten, total = dest.rewrite(source)
231+
232+
while token is not None:
233+
token, rewritten, total = dest.rewrite(source, token=token)
234+
235+
assert rewritten == len(source_data)
236+
assert dest.download_as_bytes() == source_data
237+
227238

228239
def test_blob_upload_w_bucket_cmek_enabled(
229240
kms_bucket,
Collapse file

‎tests/unit/test_blob.py‎

Copy file name to clipboardExpand all lines: tests/unit/test_blob.py
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4942,6 +4942,58 @@ def test_rewrite_same_name_w_old_key_new_kms_key(self):
49424942
_target_object=dest,
49434943
)
49444944

4945+
def test_rewrite_same_name_w_kms_key_w_version(self):
4946+
blob_name = "blob"
4947+
source_key = b"01234567890123456789012345678901" # 32 bytes
4948+
source_key_b64 = base64.b64encode(source_key).rstrip().decode("ascii")
4949+
source_key_hash = hashlib.sha256(source_key).digest()
4950+
source_key_hash_b64 = base64.b64encode(source_key_hash).rstrip().decode("ascii")
4951+
dest_kms_resource = (
4952+
"projects/test-project-123/"
4953+
"locations/us/"
4954+
"keyRings/test-ring/"
4955+
"cryptoKeys/test-key"
4956+
"cryptoKeyVersions/1"
4957+
)
4958+
bytes_rewritten = object_size = 42
4959+
api_response = {
4960+
"totalBytesRewritten": bytes_rewritten,
4961+
"objectSize": object_size,
4962+
"done": True,
4963+
"resource": {"etag": "DEADBEEF"},
4964+
}
4965+
client = mock.Mock(spec=["_post_resource"])
4966+
client._post_resource.return_value = api_response
4967+
bucket = _Bucket(client=client)
4968+
source = self._make_one(blob_name, bucket=bucket, encryption_key=source_key)
4969+
dest = self._make_one(blob_name, bucket=bucket, kms_key_name=dest_kms_resource)
4970+
4971+
token, rewritten, size = dest.rewrite(source)
4972+
4973+
self.assertIsNone(token)
4974+
self.assertEqual(rewritten, bytes_rewritten)
4975+
self.assertEqual(size, object_size)
4976+
4977+
expected_path = f"/b/name/o/{blob_name}/rewriteTo/b/name/o/{blob_name}"
4978+
expected_data = {"kmsKeyName": dest_kms_resource}
4979+
# The kmsKeyName version value can't be used in the rewrite request,
4980+
# so the client instead ignores it.
4981+
expected_query_params = {}
4982+
expected_headers = {
4983+
"X-Goog-Copy-Source-Encryption-Algorithm": "AES256",
4984+
"X-Goog-Copy-Source-Encryption-Key": source_key_b64,
4985+
"X-Goog-Copy-Source-Encryption-Key-Sha256": source_key_hash_b64,
4986+
}
4987+
client._post_resource.assert_called_once_with(
4988+
expected_path,
4989+
expected_data,
4990+
query_params=expected_query_params,
4991+
headers=expected_headers,
4992+
timeout=self._get_default_timeout(),
4993+
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
4994+
_target_object=dest,
4995+
)
4996+
49454997
def test_update_storage_class_invalid(self):
49464998
blob_name = "blob-name"
49474999
bucket = _Bucket()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.