Skip to content

samples(storage): IAM conditions samples #2730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 21, 2020
Merged
62 changes: 51 additions & 11 deletions storage/cloud-client/iam_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,76 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os

from google.cloud import storage
import pytest
import re
import time

import storage_remove_bucket_iam_member
import storage_add_bucket_iam_member
import storage_add_bucket_conditional_iam_binding
import storage_view_bucket_iam_members

BUCKET = os.environ["CLOUD_STORAGE_BUCKET"]
MEMBER = "group:[email protected]"
ROLE = "roles/storage.legacyBucketReader"

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


@pytest.fixture
def bucket():
yield storage.Client().bucket(BUCKET)
bucket_name = "test-iam-" + str(int(time.time()))
bucket = storage.Client().create_bucket(bucket_name)
bucket.iam_configuration.uniform_bucket_level_access_enabled = True
bucket.patch()
yield bucket
time.sleep(3)
bucket.delete(force=True)


def test_view_bucket_iam_members():
storage_view_bucket_iam_members.view_bucket_iam_members(BUCKET)
def test_view_bucket_iam_members(capsys, bucket):
storage_view_bucket_iam_members.view_bucket_iam_members(bucket.name)
assert re.match("Role: .*, Members: .*", capsys.readouterr().out)


def test_add_bucket_iam_member(bucket):
storage_add_bucket_iam_member.add_bucket_iam_member(BUCKET, ROLE, MEMBER)
assert MEMBER in bucket.get_iam_policy()[ROLE]
storage_add_bucket_iam_member.add_bucket_iam_member(bucket.name, ROLE, MEMBER)
policy = bucket.get_iam_policy(requested_policy_version=3)
assert any(
binding["role"] == ROLE and MEMBER in binding["members"]
for binding in policy.bindings
)


def test_add_bucket_conditional_iam_binding(bucket):
storage_add_bucket_conditional_iam_binding.add_bucket_conditional_iam_binding(
bucket.name,
ROLE,
CONDITION_TITLE,
CONDITION_DESCRIPTION,
CONDITION_EXPRESSION,
{MEMBER}
)
policy = bucket.get_iam_policy(requested_policy_version=3)
assert any(
binding["role"] == ROLE and
binding["members"] == {MEMBER} and
binding["condition"] == {
"title": CONDITION_TITLE,
"description": CONDITION_DESCRIPTION,
"expression": CONDITION_EXPRESSION
}
for binding in policy.bindings
)


def test_remove_bucket_iam_member(bucket):
storage_remove_bucket_iam_member.remove_bucket_iam_member(
BUCKET, ROLE, MEMBER
storage_remove_bucket_iam_member.remove_bucket_iam_member(bucket.name, ROLE, MEMBER)

policy = bucket.get_iam_policy(requested_policy_version=3)
assert not any(
binding["role"] == ROLE and MEMBER in binding["members"]
for binding in policy.bindings
)
assert MEMBER not in bucket.get_iam_policy()[ROLE]
2 changes: 1 addition & 1 deletion storage/cloud-client/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
google-cloud-pubsub==1.1.0
google-cloud-storage==1.23.0
google-cloud-storage==1.25.0
78 changes: 78 additions & 0 deletions storage/cloud-client/storage_add_bucket_conditional_iam_binding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env python

# Copyright 2020 Google LLC. All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys

# [START storage_add_bucket_conditional_iam_binding]
from google.cloud import storage


def add_bucket_conditional_iam_binding(
bucket_name, role, title, description, expression, members
):
"""Add a conditional IAM binding to a bucket's IAM policy."""
# bucket_name = "your-bucket-name"
# role = "IAM role, e.g. roles/storage.objectViewer"
# members = {"IAM identity, e.g. user: [email protected]}"
# title = "Condition title."
# description = "Condition description."
# expression = "Condition expression."

storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)

policy = bucket.get_iam_policy(requested_policy_version=3)

# Set the policy's version to 3 to use condition in bindings.
policy.version = 3

policy.bindings.append(
{
"role": role,
"members": members,
"condition": {
"title": title,
"description": description,
"expression": expression,
},
}
)

bucket.set_iam_policy(policy)

print("Added the following member(s) with role {} to {}:".format(role, bucket_name))

for member in members:
print(" {}".format(member))

print("with condition:")
print(" Title: {}".format(title))
print(" Description: {}".format(description))
print(" Expression: {}".format(expression))


# [END storage_add_bucket_conditional_iam_binding]


if __name__ == "__main__":
add_bucket_conditional_iam_binding(
bucket_name=sys.argv[1],
role=sys.argv[2],
title=sys.argv[3],
description=sys.argv[4],
expression=sys.argv[5],
members=set(sys.argv[6::]),
)
8 changes: 3 additions & 5 deletions storage/cloud-client/storage_add_bucket_iam_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ def add_bucket_iam_member(bucket_name, role, member):
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)

policy = bucket.get_iam_policy()
policy = bucket.get_iam_policy(requested_policy_version=3)

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

bucket.set_iam_policy(policy)

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


if __name__ == "__main__":
add_bucket_iam_member(
bucket_name=sys.argv[1], role=sys.argv[2], member=sys.argv[3]
)
add_bucket_iam_member(bucket_name=sys.argv[1], role=sys.argv[2], member=sys.argv[3])
7 changes: 5 additions & 2 deletions storage/cloud-client/storage_remove_bucket_iam_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ def remove_bucket_iam_member(bucket_name, role, member):
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)

policy = bucket.get_iam_policy()
policy = bucket.get_iam_policy(requested_policy_version=3)

policy[role].discard(member)
for binding in policy.bindings:
print(binding)
if binding["role"] == role and binding.get("condition") is None:
binding["members"].discard(member)

bucket.set_iam_policy(policy)

Expand Down
7 changes: 3 additions & 4 deletions storage/cloud-client/storage_view_bucket_iam_members.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ def view_bucket_iam_members(bucket_name):
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)

policy = bucket.get_iam_policy()
policy = bucket.get_iam_policy(requested_policy_version=3)

for role in policy:
members = policy[role]
print("Role: {}, Members: {}".format(role, members))
for binding in policy.bindings:
print("Role: {}, Members: {}".format(binding["role"], binding["members"]))


# [END storage_view_bucket_iam_members]
Expand Down