Skip to content

Commit a9e1342

Browse files
authored
samples(storage): IAM conditions samples (#2730)
* docs(storage): use policy.bindings in Storage/IAM samples * update view Bucket IAM to use policy.bindings * update remove Bucket IAM to use policy.bindings * blacken * add IAM condition sample * add conditional iam binding sample * bump storage requirement to 1.25.0 * fix tests * remove unused imports
1 parent ead0697 commit a9e1342

6 files changed

+141
-23
lines changed

storage/cloud-client/iam_test.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,76 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import os
16-
1715
from google.cloud import storage
1816
import pytest
17+
import re
18+
import time
1919

2020
import storage_remove_bucket_iam_member
2121
import storage_add_bucket_iam_member
22+
import storage_add_bucket_conditional_iam_binding
2223
import storage_view_bucket_iam_members
2324

24-
BUCKET = os.environ["CLOUD_STORAGE_BUCKET"]
2525
MEMBER = "group:[email protected]"
2626
ROLE = "roles/storage.legacyBucketReader"
2727

28+
CONDITION_TITLE = "match-prefix"
29+
CONDITION_DESCRIPTION = "Applies to objects matching a prefix"
30+
CONDITION_EXPRESSION = "resource.name.startsWith(\"projects/_/buckets/bucket-name/objects/prefix-a-\")"
31+
2832

2933
@pytest.fixture
3034
def bucket():
31-
yield storage.Client().bucket(BUCKET)
35+
bucket_name = "test-iam-" + str(int(time.time()))
36+
bucket = storage.Client().create_bucket(bucket_name)
37+
bucket.iam_configuration.uniform_bucket_level_access_enabled = True
38+
bucket.patch()
39+
yield bucket
40+
time.sleep(3)
41+
bucket.delete(force=True)
3242

3343

34-
def test_view_bucket_iam_members():
35-
storage_view_bucket_iam_members.view_bucket_iam_members(BUCKET)
44+
def test_view_bucket_iam_members(capsys, bucket):
45+
storage_view_bucket_iam_members.view_bucket_iam_members(bucket.name)
46+
assert re.match("Role: .*, Members: .*", capsys.readouterr().out)
3647

3748

3849
def test_add_bucket_iam_member(bucket):
39-
storage_add_bucket_iam_member.add_bucket_iam_member(BUCKET, ROLE, MEMBER)
40-
assert MEMBER in bucket.get_iam_policy()[ROLE]
50+
storage_add_bucket_iam_member.add_bucket_iam_member(bucket.name, ROLE, MEMBER)
51+
policy = bucket.get_iam_policy(requested_policy_version=3)
52+
assert any(
53+
binding["role"] == ROLE and MEMBER in binding["members"]
54+
for binding in policy.bindings
55+
)
56+
57+
58+
def test_add_bucket_conditional_iam_binding(bucket):
59+
storage_add_bucket_conditional_iam_binding.add_bucket_conditional_iam_binding(
60+
bucket.name,
61+
ROLE,
62+
CONDITION_TITLE,
63+
CONDITION_DESCRIPTION,
64+
CONDITION_EXPRESSION,
65+
{MEMBER}
66+
)
67+
policy = bucket.get_iam_policy(requested_policy_version=3)
68+
assert any(
69+
binding["role"] == ROLE and
70+
binding["members"] == {MEMBER} and
71+
binding["condition"] == {
72+
"title": CONDITION_TITLE,
73+
"description": CONDITION_DESCRIPTION,
74+
"expression": CONDITION_EXPRESSION
75+
}
76+
for binding in policy.bindings
77+
)
4178

4279

4380
def test_remove_bucket_iam_member(bucket):
44-
storage_remove_bucket_iam_member.remove_bucket_iam_member(
45-
BUCKET, ROLE, MEMBER
81+
storage_remove_bucket_iam_member.remove_bucket_iam_member(bucket.name, ROLE, MEMBER)
82+
83+
policy = bucket.get_iam_policy(requested_policy_version=3)
84+
assert not any(
85+
binding["role"] == ROLE and MEMBER in binding["members"]
86+
for binding in policy.bindings
4687
)
47-
assert MEMBER not in bucket.get_iam_policy()[ROLE]

storage/cloud-client/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
google-cloud-pubsub==1.1.0
2-
google-cloud-storage==1.23.0
2+
google-cloud-storage==1.25.0
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2020 Google LLC. All Rights Reserved
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the 'License');
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import sys
18+
19+
# [START storage_add_bucket_conditional_iam_binding]
20+
from google.cloud import storage
21+
22+
23+
def add_bucket_conditional_iam_binding(
24+
bucket_name, role, title, description, expression, members
25+
):
26+
"""Add a conditional IAM binding to a bucket's IAM policy."""
27+
# bucket_name = "your-bucket-name"
28+
# role = "IAM role, e.g. roles/storage.objectViewer"
29+
# members = {"IAM identity, e.g. user: [email protected]}"
30+
# title = "Condition title."
31+
# description = "Condition description."
32+
# expression = "Condition expression."
33+
34+
storage_client = storage.Client()
35+
bucket = storage_client.bucket(bucket_name)
36+
37+
policy = bucket.get_iam_policy(requested_policy_version=3)
38+
39+
# Set the policy's version to 3 to use condition in bindings.
40+
policy.version = 3
41+
42+
policy.bindings.append(
43+
{
44+
"role": role,
45+
"members": members,
46+
"condition": {
47+
"title": title,
48+
"description": description,
49+
"expression": expression,
50+
},
51+
}
52+
)
53+
54+
bucket.set_iam_policy(policy)
55+
56+
print("Added the following member(s) with role {} to {}:".format(role, bucket_name))
57+
58+
for member in members:
59+
print(" {}".format(member))
60+
61+
print("with condition:")
62+
print(" Title: {}".format(title))
63+
print(" Description: {}".format(description))
64+
print(" Expression: {}".format(expression))
65+
66+
67+
# [END storage_add_bucket_conditional_iam_binding]
68+
69+
70+
if __name__ == "__main__":
71+
add_bucket_conditional_iam_binding(
72+
bucket_name=sys.argv[1],
73+
role=sys.argv[2],
74+
title=sys.argv[3],
75+
description=sys.argv[4],
76+
expression=sys.argv[5],
77+
members=set(sys.argv[6::]),
78+
)

storage/cloud-client/storage_add_bucket_iam_member.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ def add_bucket_iam_member(bucket_name, role, member):
2929
storage_client = storage.Client()
3030
bucket = storage_client.bucket(bucket_name)
3131

32-
policy = bucket.get_iam_policy()
32+
policy = bucket.get_iam_policy(requested_policy_version=3)
3333

34-
policy[role].add(member)
34+
policy.bindings.append({"role": role, "members": {member}})
3535

3636
bucket.set_iam_policy(policy)
3737

@@ -42,6 +42,4 @@ def add_bucket_iam_member(bucket_name, role, member):
4242

4343

4444
if __name__ == "__main__":
45-
add_bucket_iam_member(
46-
bucket_name=sys.argv[1], role=sys.argv[2], member=sys.argv[3]
47-
)
45+
add_bucket_iam_member(bucket_name=sys.argv[1], role=sys.argv[2], member=sys.argv[3])

storage/cloud-client/storage_remove_bucket_iam_member.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ def remove_bucket_iam_member(bucket_name, role, member):
2929
storage_client = storage.Client()
3030
bucket = storage_client.bucket(bucket_name)
3131

32-
policy = bucket.get_iam_policy()
32+
policy = bucket.get_iam_policy(requested_policy_version=3)
3333

34-
policy[role].discard(member)
34+
for binding in policy.bindings:
35+
print(binding)
36+
if binding["role"] == role and binding.get("condition") is None:
37+
binding["members"].discard(member)
3538

3639
bucket.set_iam_policy(policy)
3740

storage/cloud-client/storage_view_bucket_iam_members.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,10 @@ def view_bucket_iam_members(bucket_name):
2727
storage_client = storage.Client()
2828
bucket = storage_client.bucket(bucket_name)
2929

30-
policy = bucket.get_iam_policy()
30+
policy = bucket.get_iam_policy(requested_policy_version=3)
3131

32-
for role in policy:
33-
members = policy[role]
34-
print("Role: {}, Members: {}".format(role, members))
32+
for binding in policy.bindings:
33+
print("Role: {}, Members: {}".format(binding["role"], binding["members"]))
3534

3635

3736
# [END storage_view_bucket_iam_members]

0 commit comments

Comments
 (0)