diff --git a/docs/pubsub-usage.rst b/docs/pubsub-usage.rst index 5c608530889b..28a21c27ad6d 100644 --- a/docs/pubsub-usage.rst +++ b/docs/pubsub-usage.rst @@ -311,3 +311,21 @@ Fetch messages for a pull subscription without blocking (none pending): >>> messages = [recv[1] for recv in received] >>> [message.message_id for message in messages] [] + +Fetch the IAM policy for a subscription + +.. doctest:: + + >>> from gcloud import pubsub + >>> client = pubsub.Client() + >>> topic = client.topic('topic_name') + >>> subscription = topic.subscription('subscription_name') + >>> policy = subscription.get_iam_policy() # API request + >>> policy.etag + 'DEADBEEF' + >>> policy.owners + ['user:phred@example.com'] + >>> policy.writers + ['systemAccount:abc-1234@systemaccounts.example.com'] + >>> policy.readers + ['domain:example.com'] diff --git a/gcloud/pubsub/subscription.py b/gcloud/pubsub/subscription.py index e3da8a06f5b4..5802266488b7 100644 --- a/gcloud/pubsub/subscription.py +++ b/gcloud/pubsub/subscription.py @@ -16,6 +16,7 @@ from gcloud.exceptions import NotFound from gcloud.pubsub._helpers import topic_name_from_path +from gcloud.pubsub.iam import Policy from gcloud.pubsub.message import Message @@ -263,3 +264,22 @@ def delete(self, client=None): """ client = self._require_client(client) client.connection.api_request(method='DELETE', path=self.path) + + def get_iam_policy(self, client=None): + """Fetch the IAM policy for the subscription. + + See: + https://cloud.google.com/pubsub/reference/rest/v1/projects.subscriptions/getIamPolicy + + :type client: :class:`gcloud.pubsub.client.Client` or ``NoneType`` + :param client: the client to use. If not passed, falls back to the + ``client`` stored on the current subscription's topic. + + :rtype: :class:`gcloud.pubsub.iam.Policy` + :returns: policy created from the resource returned by the + ``getIamPolicy`` API request. + """ + client = self._require_client(client) + path = '%s:getIamPolicy' % (self.path,) + resp = client.connection.api_request(method='GET', path=path) + return Policy.from_api_repr(resp) diff --git a/gcloud/pubsub/test_subscription.py b/gcloud/pubsub/test_subscription.py index 494454c15b21..72b3d80753ce 100644 --- a/gcloud/pubsub/test_subscription.py +++ b/gcloud/pubsub/test_subscription.py @@ -484,6 +484,77 @@ def test_delete_w_alternate_client(self): self.assertEqual(req['method'], 'DELETE') self.assertEqual(req['path'], '/%s' % SUB_PATH) + def test_get_iam_policy_w_bound_client(self): + OWNER1 = 'user:phred@example.com' + OWNER2 = 'group:cloud-logs@google.com' + WRITER1 = 'domain:google.com' + WRITER2 = 'user:phred@example.com' + READER1 = 'serviceAccount:1234-abcdef@service.example.com' + READER2 = 'user:phred@example.com' + POLICY = { + 'etag': 'DEADBEEF', + 'version': 17, + 'bindings': [ + {'role': 'roles/owner', 'members': [OWNER1, OWNER2]}, + {'role': 'roles/writer', 'members': [WRITER1, WRITER2]}, + {'role': 'roles/reader', 'members': [READER1, READER2]}, + ], + } + PROJECT = 'PROJECT' + TOPIC_NAME = 'topic_name' + SUB_NAME = 'sub_name' + PATH = 'projects/%s/subscriptions/%s:getIamPolicy' % ( + PROJECT, SUB_NAME) + + conn = _Connection(POLICY) + CLIENT = _Client(project=PROJECT, connection=conn) + topic = _Topic(TOPIC_NAME, client=CLIENT) + subscription = self._makeOne(SUB_NAME, topic) + + policy = subscription.get_iam_policy() + + self.assertEqual(policy.etag, 'DEADBEEF') + self.assertEqual(policy.version, 17) + self.assertEqual(sorted(policy.owners), [OWNER2, OWNER1]) + self.assertEqual(sorted(policy.writers), [WRITER1, WRITER2]) + self.assertEqual(sorted(policy.readers), [READER1, READER2]) + + self.assertEqual(len(conn._requested), 1) + req = conn._requested[0] + self.assertEqual(req['method'], 'GET') + self.assertEqual(req['path'], '/%s' % PATH) + + def test_get_iam_policy_w_alternate_client(self): + POLICY = { + 'etag': 'ACAB', + } + PROJECT = 'PROJECT' + TOPIC_NAME = 'topic_name' + SUB_NAME = 'sub_name' + PATH = 'projects/%s/subscriptions/%s:getIamPolicy' % ( + PROJECT, SUB_NAME) + + conn1 = _Connection() + conn2 = _Connection(POLICY) + CLIENT1 = _Client(project=PROJECT, connection=conn1) + CLIENT2 = _Client(project=PROJECT, connection=conn2) + topic = _Topic(TOPIC_NAME, client=CLIENT1) + subscription = self._makeOne(SUB_NAME, topic) + + policy = subscription.get_iam_policy(client=CLIENT2) + + self.assertEqual(policy.etag, 'ACAB') + self.assertEqual(policy.version, None) + self.assertEqual(sorted(policy.owners), []) + self.assertEqual(sorted(policy.writers), []) + self.assertEqual(sorted(policy.readers), []) + + self.assertEqual(len(conn1._requested), 0) + self.assertEqual(len(conn2._requested), 1) + req = conn2._requested[0] + self.assertEqual(req['method'], 'GET') + self.assertEqual(req['path'], '/%s' % PATH) + class _Connection(object):