Skip to content

Commit 62b0f9e

Browse files
shaffeeullahcojenco
authored andcommitted
feat: add public access prevention to bucket IAM configuration (googleapis#304)
1 parent 9dcd8b9 commit 62b0f9e

File tree

4 files changed

+145
-1
lines changed

4 files changed

+145
-1
lines changed

google/cloud/storage/bucket.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from google.cloud.storage.constants import MULTI_REGIONAL_LEGACY_STORAGE_CLASS
5151
from google.cloud.storage.constants import MULTI_REGION_LOCATION_TYPE
5252
from google.cloud.storage.constants import NEARLINE_STORAGE_CLASS
53+
from google.cloud.storage.constants import PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
5354
from google.cloud.storage.constants import REGIONAL_LEGACY_STORAGE_CLASS
5455
from google.cloud.storage.constants import REGION_LOCATION_TYPE
5556
from google.cloud.storage.constants import STANDARD_STORAGE_CLASS
@@ -383,6 +384,12 @@ class IAMConfiguration(dict):
383384
:type bucket: :class:`Bucket`
384385
:params bucket: Bucket for which this instance is the policy.
385386
387+
:type public_access_prevention: str
388+
:params public_access_prevention:
389+
(Optional) Whether the public access prevention policy is 'unspecified' (default) or 'enforced'
390+
See: https://cloud.google.com/storage/docs/public-access-prevention
391+
See: https://cloud.google.com/storage/docs/public-access-prevention
392+
386393
:type uniform_bucket_level_access_enabled: bool
387394
:params bucket_policy_only_enabled:
388395
(Optional) Whether the IAM-only policy is enabled for the bucket.
@@ -404,6 +411,7 @@ class IAMConfiguration(dict):
404411
def __init__(
405412
self,
406413
bucket,
414+
public_access_prevention=_default,
407415
uniform_bucket_level_access_enabled=_default,
408416
uniform_bucket_level_access_locked_time=_default,
409417
bucket_policy_only_enabled=_default,
@@ -428,8 +436,14 @@ def __init__(
428436
if uniform_bucket_level_access_enabled is _default:
429437
uniform_bucket_level_access_enabled = False
430438

439+
if public_access_prevention is _default:
440+
public_access_prevention = PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
441+
431442
data = {
432-
"uniformBucketLevelAccess": {"enabled": uniform_bucket_level_access_enabled}
443+
"uniformBucketLevelAccess": {
444+
"enabled": uniform_bucket_level_access_enabled
445+
},
446+
"publicAccessPrevention": public_access_prevention,
433447
}
434448
if uniform_bucket_level_access_locked_time is not _default:
435449
data["uniformBucketLevelAccess"]["lockedTime"] = _datetime_to_rfc3339(
@@ -464,6 +478,21 @@ def bucket(self):
464478
"""
465479
return self._bucket
466480

481+
@property
482+
def public_access_prevention(self):
483+
"""Setting for public access prevention policy. Options are 'unspecified' (default) or 'enforced'.
484+
More information can be found at https://cloud.google.com/storage/docs/public-access-prevention
485+
486+
:rtype: string
487+
:returns: the public access prevention status, either 'enforced' or 'unspecified'.
488+
"""
489+
return self["publicAccessPrevention"]
490+
491+
@public_access_prevention.setter
492+
def public_access_prevention(self, value):
493+
self["publicAccessPrevention"] = value
494+
self.bucket._patch_property("iamConfiguration", self)
495+
467496
@property
468497
def uniform_bucket_level_access_enabled(self):
469498
"""If set, access checks only use bucket-level IAM policies or above.

google/cloud/storage/constants.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,16 @@
9696
_DEFAULT_TIMEOUT = 60 # in seconds
9797
"""The default request timeout in seconds if a timeout is not explicitly given.
9898
"""
99+
100+
# Public Access Prevention
101+
PUBLIC_ACCESS_PREVENTION_ENFORCED = "enforced"
102+
"""Enforced public access prevention value.
103+
104+
See: https://cloud.google.com/storage/docs/public-access-prevention
105+
"""
106+
107+
PUBLIC_ACCESS_PREVENTION_UNSPECIFIED = "unspecified"
108+
"""Unspecified public access prevention value.
109+
110+
See: https://cloud.google.com/storage/docs/public-access-prevention
111+
"""

tests/system/test_bucket.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,3 +766,82 @@ def test_ubla_set_unset_preserves_acls(
766766

767767
assert bucket_acl_before == bucket_acl_after
768768
assert blob_acl_before == blob_acl_after
769+
770+
771+
def test_new_bucket_created_w_unspecified_pap(
772+
storage_client, buckets_to_delete, blobs_to_delete,
773+
):
774+
from google.cloud.storage import constants
775+
776+
bucket_name = _helpers.unique_name("new-w-pap-unspecified")
777+
bucket = storage_client.bucket(bucket_name)
778+
bucket.iam_configuration.uniform_bucket_level_access_enabled = True
779+
bucket.create()
780+
buckets_to_delete.append(bucket)
781+
782+
assert (
783+
bucket.iam_configuration.public_access_prevention
784+
== constants.PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
785+
)
786+
787+
bucket.iam_configuration.public_access_prevention = (
788+
constants.PUBLIC_ACCESS_PREVENTION_ENFORCED
789+
)
790+
bucket.patch()
791+
assert (
792+
bucket.iam_configuration.public_access_prevention
793+
== constants.PUBLIC_ACCESS_PREVENTION_ENFORCED
794+
)
795+
assert bucket.iam_configuration.uniform_bucket_level_access_enabled
796+
797+
bucket.iam_configuration.uniform_bucket_level_access_enabled = False
798+
bucket.patch()
799+
assert (
800+
bucket.iam_configuration.public_access_prevention
801+
== constants.PUBLIC_ACCESS_PREVENTION_ENFORCED
802+
)
803+
804+
with pytest.raises(exceptions.BadRequest):
805+
bucket.iam_configuration.public_access_prevention = "unexpected value"
806+
bucket.patch()
807+
808+
with pytest.raises(exceptions.PreconditionFailed):
809+
bucket.make_public()
810+
811+
blob_name = "my-blob.txt"
812+
blob = bucket.blob(blob_name)
813+
payload = b"DEADBEEF"
814+
blob.upload_from_string(payload)
815+
816+
with pytest.raises(exceptions.PreconditionFailed):
817+
blob.make_public()
818+
819+
820+
def test_new_bucket_created_w_enforced_pap(
821+
storage_client, buckets_to_delete, blobs_to_delete,
822+
):
823+
from google.cloud.storage import constants
824+
825+
bucket_name = _helpers.unique_name("new-w-pap-enforced")
826+
bucket = storage_client.bucket(bucket_name)
827+
bucket.iam_configuration.public_access_prevention = (
828+
constants.PUBLIC_ACCESS_PREVENTION_ENFORCED
829+
)
830+
bucket.create()
831+
buckets_to_delete.append(bucket)
832+
833+
assert (
834+
bucket.iam_configuration.public_access_prevention
835+
== constants.PUBLIC_ACCESS_PREVENTION_ENFORCED
836+
)
837+
838+
bucket.iam_configuration.public_access_prevention = (
839+
constants.PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
840+
)
841+
bucket.patch()
842+
843+
assert (
844+
bucket.iam_configuration.public_access_prevention
845+
== constants.PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
846+
)
847+
assert not bucket.iam_configuration.uniform_bucket_level_access_enabled

tests/unit/test_bucket.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
from google.cloud.storage.retry import DEFAULT_RETRY_IF_ETAG_IN_JSON
2323
from google.cloud.storage.retry import DEFAULT_RETRY_IF_GENERATION_SPECIFIED
2424
from google.cloud.storage.retry import DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED
25+
from google.cloud.storage.constants import PUBLIC_ACCESS_PREVENTION_ENFORCED
26+
from google.cloud.storage.constants import PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
2527

2628

2729
def _create_signing_credentials():
@@ -356,6 +358,9 @@ def test_ctor_defaults(self):
356358
self.assertIs(config.bucket, bucket)
357359
self.assertFalse(config.uniform_bucket_level_access_enabled)
358360
self.assertIsNone(config.uniform_bucket_level_access_locked_time)
361+
self.assertEqual(
362+
config.public_access_prevention, PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
363+
)
359364
self.assertFalse(config.bucket_policy_only_enabled)
360365
self.assertIsNone(config.bucket_policy_only_locked_time)
361366

@@ -378,6 +383,24 @@ def test_ctor_explicit_ubla(self):
378383
self.assertTrue(config.bucket_policy_only_enabled)
379384
self.assertEqual(config.bucket_policy_only_locked_time, now)
380385

386+
def test_ctor_explicit_pap(self):
387+
bucket = self._make_bucket()
388+
389+
config = self._make_one(
390+
bucket, public_access_prevention=PUBLIC_ACCESS_PREVENTION_ENFORCED,
391+
)
392+
393+
self.assertIs(config.bucket, bucket)
394+
self.assertFalse(config.uniform_bucket_level_access_enabled)
395+
self.assertEqual(
396+
config.public_access_prevention, PUBLIC_ACCESS_PREVENTION_ENFORCED
397+
)
398+
399+
config.public_access_prevention = PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
400+
self.assertEqual(
401+
config.public_access_prevention, PUBLIC_ACCESS_PREVENTION_UNSPECIFIED
402+
)
403+
381404
def test_ctor_explicit_bpo(self):
382405
import datetime
383406
import pytz

0 commit comments

Comments
 (0)