Skip to content
This repository was archived by the owner on Mar 31, 2026. It is now read-only.

Commit 2a6e8b0

Browse files
rajeevpodargoogle-labs-jules[bot]nidhiii-27
authored
feat: add support for bucket encryption enforcement config (#1742)
Implemented bucket encryption enforcement configuration support. Added `EncryptionEnforcementConfig` and `BucketEncryption` classes. Added `encryption` property to `Bucket`. Added unit tests. --- *PR created automatically by Jules for task [10151670798613550332](https://jules.google.com/task/10151670798613550332) started by @rajeevpodar* --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: rajeevpodar <3637722+rajeevpodar@users.noreply.github.com> Co-authored-by: Nidhi Nandwani <nidhiii@google.com>
1 parent 141f7ac commit 2a6e8b0

File tree

4 files changed

+531
-2
lines changed

4 files changed

+531
-2
lines changed

google/cloud/storage/bucket.py

Lines changed: 265 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
from google.cloud.storage.acl import DefaultObjectACL
4444
from google.cloud.storage.blob import _quote
4545
from google.cloud.storage.blob import Blob
46-
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
46+
from google.cloud.storage.constants import (
47+
_DEFAULT_TIMEOUT,
48+
ENFORCEMENT_MODE_FULLY_RESTRICTED,
49+
ENFORCEMENT_MODE_NOT_RESTRICTED,
50+
)
4751
from google.cloud.storage.constants import ARCHIVE_STORAGE_CLASS
4852
from google.cloud.storage.constants import COLDLINE_STORAGE_CLASS
4953
from google.cloud.storage.constants import DUAL_REGION_LOCATION_TYPE
@@ -65,7 +69,6 @@
6569
from google.cloud.storage.retry import DEFAULT_RETRY_IF_ETAG_IN_JSON
6670
from google.cloud.storage.retry import DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED
6771

68-
6972
_UBLA_BPO_ENABLED_MESSAGE = (
7073
"Pass only one of 'uniform_bucket_level_access_enabled' / "
7174
"'bucket_policy_only_enabled' to 'IAMConfiguration'."
@@ -2538,6 +2541,25 @@ def cors(self, entries):
25382541
:rtype: bool or ``NoneType``
25392542
"""
25402543

2544+
@property
2545+
def encryption(self):
2546+
"""Retrieve encryption configuration for this bucket.
2547+
2548+
:rtype: :class:`BucketEncryption`
2549+
:returns: an instance for managing the bucket's encryption configuration.
2550+
"""
2551+
info = self._properties.get("encryption", {})
2552+
return BucketEncryption.from_api_repr(info, self)
2553+
2554+
@encryption.setter
2555+
def encryption(self, value):
2556+
"""Set encryption configuration for this bucket.
2557+
2558+
:type value: :class:`BucketEncryption` or dict
2559+
:param value: The encryption configuration.
2560+
"""
2561+
self._patch_property("encryption", value)
2562+
25412563
@property
25422564
def default_kms_key_name(self):
25432565
"""Retrieve / set default KMS encryption key for objects in the bucket.
@@ -3965,6 +3987,247 @@ def ip_filter(self, value):
39653987
self._patch_property(_IP_FILTER_PROPERTY, value)
39663988

39673989

3990+
class EncryptionEnforcementConfig(dict):
3991+
"""Map a bucket's encryption enforcement configuration.
3992+
3993+
:type restriction_mode: str
3994+
:param restriction_mode:
3995+
(Optional) The restriction mode for the encryption type.
3996+
When set to ``FullyRestricted``, the bucket will only allow objects encrypted with the encryption type corresponding to this configuration.
3997+
When set to ``NotRestricted``, the bucket will allow objects encrypted with any encryption type.
3998+
3999+
:type effective_time: :class:`datetime.datetime`
4000+
:param effective_time:
4001+
(Output only) The time when the encryption enforcement configuration became effective.
4002+
"""
4003+
4004+
def __init__(self, restriction_mode=None):
4005+
data = {}
4006+
if restriction_mode is not None:
4007+
# Validate input against allowed constants
4008+
allowed = (
4009+
ENFORCEMENT_MODE_FULLY_RESTRICTED,
4010+
ENFORCEMENT_MODE_NOT_RESTRICTED,
4011+
)
4012+
if restriction_mode not in allowed:
4013+
raise ValueError(
4014+
f"Invalid restriction_mode: {restriction_mode}. "
4015+
f"Must be one of {allowed}"
4016+
)
4017+
data["restrictionMode"] = restriction_mode
4018+
4019+
super().__init__(data)
4020+
4021+
@classmethod
4022+
def from_api_repr(cls, resource):
4023+
"""Factory: construct instance from resource.
4024+
4025+
:type resource: dict
4026+
:param resource: mapping as returned from API call.
4027+
4028+
:rtype: :class:`EncryptionEnforcementConfig`
4029+
:returns: Instance created from resource.
4030+
"""
4031+
instance = cls()
4032+
instance.update(resource)
4033+
return instance
4034+
4035+
@property
4036+
def restriction_mode(self):
4037+
"""Get the restriction mode.
4038+
4039+
:rtype: str or ``NoneType``
4040+
:returns: The restriction mode or ``None`` if the property is not set.
4041+
"""
4042+
return self.get("restrictionMode")
4043+
4044+
@restriction_mode.setter
4045+
def restriction_mode(self, value):
4046+
"""Set the restriction mode.
4047+
4048+
:type value: str
4049+
:param value: The restriction mode.
4050+
"""
4051+
self["restrictionMode"] = value
4052+
4053+
@property
4054+
def effective_time(self):
4055+
"""Get the effective time.
4056+
4057+
:rtype: datetime.datetime or ``NoneType``
4058+
:returns: point-in time at which the configuration is effective,
4059+
or ``None`` if the property is not set.
4060+
"""
4061+
timestamp = self.get("effectiveTime")
4062+
if timestamp is not None:
4063+
return _rfc3339_nanos_to_datetime(timestamp)
4064+
4065+
4066+
class BucketEncryption(dict):
4067+
"""Map a bucket's encryption configuration.
4068+
4069+
:type bucket: :class:`Bucket`
4070+
:param bucket: Bucket for which this instance is the policy.
4071+
4072+
:type default_kms_key_name: str
4073+
:param default_kms_key_name:
4074+
(Optional) Resource name of KMS key used to encrypt bucket's content.
4075+
4076+
:type google_managed_encryption_enforcement_config: :class:`EncryptionEnforcementConfig`
4077+
:param google_managed_encryption_enforcement_config:
4078+
(Optional) Encryption enforcement configuration for Google managed encryption.
4079+
4080+
:type customer_managed_encryption_enforcement_config: :class:`EncryptionEnforcementConfig`
4081+
:param customer_managed_encryption_enforcement_config:
4082+
(Optional) Encryption enforcement configuration for Customer managed encryption.
4083+
4084+
:type customer_supplied_encryption_enforcement_config: :class:`EncryptionEnforcementConfig`
4085+
:param customer_supplied_encryption_enforcement_config:
4086+
(Optional) Encryption enforcement configuration for Customer supplied encryption.
4087+
"""
4088+
4089+
def __init__(
4090+
self,
4091+
bucket,
4092+
default_kms_key_name=None,
4093+
google_managed_encryption_enforcement_config=None,
4094+
customer_managed_encryption_enforcement_config=None,
4095+
customer_supplied_encryption_enforcement_config=None,
4096+
):
4097+
data = {}
4098+
if default_kms_key_name is not None:
4099+
data["defaultKmsKeyName"] = default_kms_key_name
4100+
4101+
if google_managed_encryption_enforcement_config is not None:
4102+
data["googleManagedEncryptionEnforcementConfig"] = (
4103+
google_managed_encryption_enforcement_config
4104+
)
4105+
4106+
if customer_managed_encryption_enforcement_config is not None:
4107+
data["customerManagedEncryptionEnforcementConfig"] = (
4108+
customer_managed_encryption_enforcement_config
4109+
)
4110+
4111+
if customer_supplied_encryption_enforcement_config is not None:
4112+
data["customerSuppliedEncryptionEnforcementConfig"] = (
4113+
customer_supplied_encryption_enforcement_config
4114+
)
4115+
4116+
super().__init__(data)
4117+
self._bucket = bucket
4118+
4119+
@classmethod
4120+
def from_api_repr(cls, resource, bucket):
4121+
"""Factory: construct instance from resource.
4122+
4123+
:type resource: dict
4124+
:param resource: mapping as returned from API call.
4125+
4126+
:type bucket: :class:`Bucket`
4127+
:params bucket: Bucket for which this instance is the policy.
4128+
4129+
:rtype: :class:`BucketEncryption`
4130+
:returns: Instance created from resource.
4131+
"""
4132+
instance = cls(bucket)
4133+
instance.update(resource)
4134+
return instance
4135+
4136+
@property
4137+
def bucket(self):
4138+
"""Bucket for which this instance is the policy.
4139+
4140+
:rtype: :class:`Bucket`
4141+
:returns: the instance's bucket.
4142+
"""
4143+
return self._bucket
4144+
4145+
@property
4146+
def default_kms_key_name(self):
4147+
"""Retrieve default KMS encryption key for objects in the bucket.
4148+
4149+
:rtype: str or ``NoneType``
4150+
:returns: Default KMS encryption key, or ``None`` if not set.
4151+
"""
4152+
return self.get("defaultKmsKeyName")
4153+
4154+
@default_kms_key_name.setter
4155+
def default_kms_key_name(self, value):
4156+
"""Set default KMS encryption key for objects in the bucket.
4157+
4158+
:type value: str or None
4159+
:param value: new KMS key name (None to clear any existing key).
4160+
"""
4161+
self["defaultKmsKeyName"] = value
4162+
self.bucket._patch_property("encryption", self)
4163+
4164+
@property
4165+
def google_managed_encryption_enforcement_config(self):
4166+
"""Retrieve the encryption enforcement configuration for Google managed encryption.
4167+
4168+
:rtype: :class:`EncryptionEnforcementConfig`
4169+
:returns: The configuration instance.
4170+
"""
4171+
data = self.get("googleManagedEncryptionEnforcementConfig")
4172+
if data:
4173+
return EncryptionEnforcementConfig.from_api_repr(data)
4174+
return None
4175+
4176+
@google_managed_encryption_enforcement_config.setter
4177+
def google_managed_encryption_enforcement_config(self, value):
4178+
"""Set the encryption enforcement configuration for Google managed encryption.
4179+
4180+
:type value: :class:`EncryptionEnforcementConfig` or dict
4181+
:param value: The configuration instance or dictionary.
4182+
"""
4183+
self["googleManagedEncryptionEnforcementConfig"] = value
4184+
self.bucket._patch_property("encryption", self)
4185+
4186+
@property
4187+
def customer_managed_encryption_enforcement_config(self):
4188+
"""Retrieve the encryption enforcement configuration for Customer managed encryption.
4189+
4190+
:rtype: :class:`EncryptionEnforcementConfig`
4191+
:returns: The configuration instance.
4192+
"""
4193+
data = self.get("customerManagedEncryptionEnforcementConfig")
4194+
if data:
4195+
return EncryptionEnforcementConfig.from_api_repr(data)
4196+
return None
4197+
4198+
@customer_managed_encryption_enforcement_config.setter
4199+
def customer_managed_encryption_enforcement_config(self, value):
4200+
"""Set the encryption enforcement configuration for Customer managed encryption.
4201+
4202+
:type value: :class:`EncryptionEnforcementConfig` or dict
4203+
:param value: The configuration instance or dictionary.
4204+
"""
4205+
self["customerManagedEncryptionEnforcementConfig"] = value
4206+
self.bucket._patch_property("encryption", self)
4207+
4208+
@property
4209+
def customer_supplied_encryption_enforcement_config(self):
4210+
"""Retrieve the encryption enforcement configuration for Customer supplied encryption.
4211+
4212+
:rtype: :class:`EncryptionEnforcementConfig`
4213+
:returns: The configuration instance.
4214+
"""
4215+
data = self.get("customerSuppliedEncryptionEnforcementConfig")
4216+
if data:
4217+
return EncryptionEnforcementConfig.from_api_repr(data)
4218+
return None
4219+
4220+
@customer_supplied_encryption_enforcement_config.setter
4221+
def customer_supplied_encryption_enforcement_config(self, value):
4222+
"""Set the encryption enforcement configuration for Customer supplied encryption.
4223+
4224+
:type value: :class:`EncryptionEnforcementConfig` or dict
4225+
:param value: The configuration instance or dictionary.
4226+
"""
4227+
self["customerSuppliedEncryptionEnforcementConfig"] = value
4228+
self.bucket._patch_property("encryption", self)
4229+
4230+
39684231
class SoftDeletePolicy(dict):
39694232
"""Map a bucket's soft delete policy.
39704233

google/cloud/storage/constants.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,9 @@
137137
138138
See: https://cloud.google.com/storage/docs/managing-turbo-replication
139139
"""
140+
141+
ENFORCEMENT_MODE_FULLY_RESTRICTED = "FullyRestricted"
142+
"""Bucket encryption restriction mode where encryption is fully restricted."""
143+
144+
ENFORCEMENT_MODE_NOT_RESTRICTED = "NotRestricted"
145+
"""Bucket encryption restriction mode where encryption is not restricted."""

0 commit comments

Comments
 (0)