From 1c8e960015657023f8d1d47badd64366351c0c38 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Fri, 30 Nov 2018 14:17:08 -0800 Subject: [PATCH 1/5] remove iam module from core --- core/google/cloud/iam.py | 248 ------------------------------- core/tests/unit/test_iam.py | 281 ------------------------------------ 2 files changed, 529 deletions(-) delete mode 100644 core/google/cloud/iam.py delete mode 100644 core/tests/unit/test_iam.py diff --git a/core/google/cloud/iam.py b/core/google/cloud/iam.py deleted file mode 100644 index c17bddcd9dfd..000000000000 --- a/core/google/cloud/iam.py +++ /dev/null @@ -1,248 +0,0 @@ -# Copyright 2017 Google LLC -# -# 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. -"""Non-API-specific IAM policy definitions - -For allowed roles / permissions, see: -https://cloud.google.com/iam/docs/understanding-roles -""" - -import collections -try: - from collections import abc as collections_abc -except ImportError: # Python 2.7 - import collections as collections_abc -import warnings - -# Generic IAM roles - -OWNER_ROLE = "roles/owner" -"""Generic role implying all rights to an object.""" - -EDITOR_ROLE = "roles/editor" -"""Generic role implying rights to modify an object.""" - -VIEWER_ROLE = "roles/viewer" -"""Generic role implying rights to access an object.""" - -_ASSIGNMENT_DEPRECATED_MSG = """\ -Assigning to '{}' is deprecated. Replace with 'policy[{}] = members.""" - - -class Policy(collections_abc.MutableMapping): - """IAM Policy - - See - https://cloud.google.com/iam/reference/rest/v1/Policy - - :type etag: str - :param etag: ETag used to identify a unique of the policy - - :type version: int - :param version: unique version of the policy - """ - - _OWNER_ROLES = (OWNER_ROLE,) - """Roles mapped onto our ``owners`` attribute.""" - - _EDITOR_ROLES = (EDITOR_ROLE,) - """Roles mapped onto our ``editors`` attribute.""" - - _VIEWER_ROLES = (VIEWER_ROLE,) - """Roles mapped onto our ``viewers`` attribute.""" - - def __init__(self, etag=None, version=None): - self.etag = etag - self.version = version - self._bindings = collections.defaultdict(set) - - def __iter__(self): - return iter(self._bindings) - - def __len__(self): - return len(self._bindings) - - def __getitem__(self, key): - return self._bindings[key] - - def __setitem__(self, key, value): - self._bindings[key] = set(value) - - def __delitem__(self, key): - del self._bindings[key] - - @property - def owners(self): - """Legacy access to owner role.""" - result = set() - for role in self._OWNER_ROLES: - for member in self._bindings.get(role, ()): - result.add(member) - return frozenset(result) - - @owners.setter - def owners(self, value): - """Update owners.""" - warnings.warn( - _ASSIGNMENT_DEPRECATED_MSG.format("owners", OWNER_ROLE), DeprecationWarning - ) - self[OWNER_ROLE] = value - - @property - def editors(self): - """Legacy access to editor role.""" - result = set() - for role in self._EDITOR_ROLES: - for member in self._bindings.get(role, ()): - result.add(member) - return frozenset(result) - - @editors.setter - def editors(self, value): - """Update editors.""" - warnings.warn( - _ASSIGNMENT_DEPRECATED_MSG.format("editors", EDITOR_ROLE), - DeprecationWarning, - ) - self[EDITOR_ROLE] = value - - @property - def viewers(self): - """Legacy access to viewer role.""" - result = set() - for role in self._VIEWER_ROLES: - for member in self._bindings.get(role, ()): - result.add(member) - return frozenset(result) - - @viewers.setter - def viewers(self, value): - """Update viewers.""" - warnings.warn( - _ASSIGNMENT_DEPRECATED_MSG.format("viewers", VIEWER_ROLE), - DeprecationWarning, - ) - self[VIEWER_ROLE] = value - - @staticmethod - def user(email): - """Factory method for a user member. - - :type email: str - :param email: E-mail for this particular user. - - :rtype: str - :returns: A member string corresponding to the given user. - """ - return "user:%s" % (email,) - - @staticmethod - def service_account(email): - """Factory method for a service account member. - - :type email: str - :param email: E-mail for this particular service account. - - :rtype: str - :returns: A member string corresponding to the given service account. - """ - return "serviceAccount:%s" % (email,) - - @staticmethod - def group(email): - """Factory method for a group member. - - :type email: str - :param email: An id or e-mail for this particular group. - - :rtype: str - :returns: A member string corresponding to the given group. - """ - return "group:%s" % (email,) - - @staticmethod - def domain(domain): - """Factory method for a domain member. - - :type domain: str - :param domain: The domain for this member. - - :rtype: str - :returns: A member string corresponding to the given domain. - """ - return "domain:%s" % (domain,) - - @staticmethod - def all_users(): - """Factory method for a member representing all users. - - :rtype: str - :returns: A member string representing all users. - """ - return "allUsers" - - @staticmethod - def authenticated_users(): - """Factory method for a member representing all authenticated users. - - :rtype: str - :returns: A member string representing all authenticated users. - """ - return "allAuthenticatedUsers" - - @classmethod - def from_api_repr(cls, resource): - """Create a policy from the resource returned from the API. - - :type resource: dict - :param resource: resource returned from the ``getIamPolicy`` API. - - :rtype: :class:`Policy` - :returns: the parsed policy - """ - version = resource.get("version") - etag = resource.get("etag") - policy = cls(etag, version) - for binding in resource.get("bindings", ()): - role = binding["role"] - members = sorted(binding["members"]) - policy[role] = members - return policy - - def to_api_repr(self): - """Construct a Policy resource. - - :rtype: dict - :returns: a resource to be passed to the ``setIamPolicy`` API. - """ - resource = {} - - if self.etag is not None: - resource["etag"] = self.etag - - if self.version is not None: - resource["version"] = self.version - - if self._bindings: - bindings = resource["bindings"] = [] - for role, members in sorted(self._bindings.items()): - if members: - bindings.append({"role": role, "members": sorted(set(members))}) - - if not bindings: - del resource["bindings"] - - return resource - - -collections_abc.MutableMapping.register(Policy) diff --git a/core/tests/unit/test_iam.py b/core/tests/unit/test_iam.py deleted file mode 100644 index 39b7595cf8b0..000000000000 --- a/core/tests/unit/test_iam.py +++ /dev/null @@ -1,281 +0,0 @@ -# Copyright 2017 Google LLC -# -# 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 unittest - - -class TestPolicy(unittest.TestCase): - @staticmethod - def _get_target_class(): - from google.cloud.iam import Policy - - return Policy - - def _make_one(self, *args, **kw): - return self._get_target_class()(*args, **kw) - - def test_ctor_defaults(self): - empty = frozenset() - policy = self._make_one() - self.assertIsNone(policy.etag) - self.assertIsNone(policy.version) - self.assertEqual(policy.owners, empty) - self.assertEqual(policy.editors, empty) - self.assertEqual(policy.viewers, empty) - self.assertEqual(len(policy), 0) - self.assertEqual(dict(policy), {}) - - def test_ctor_explicit(self): - VERSION = 17 - ETAG = "ETAG" - empty = frozenset() - policy = self._make_one(ETAG, VERSION) - self.assertEqual(policy.etag, ETAG) - self.assertEqual(policy.version, VERSION) - self.assertEqual(policy.owners, empty) - self.assertEqual(policy.editors, empty) - self.assertEqual(policy.viewers, empty) - self.assertEqual(len(policy), 0) - self.assertEqual(dict(policy), {}) - - def test___getitem___miss(self): - policy = self._make_one() - self.assertEqual(policy["nonesuch"], set()) - - def test___setitem__(self): - USER = "user:phred@example.com" - PRINCIPALS = set([USER]) - policy = self._make_one() - policy["rolename"] = [USER] - self.assertEqual(policy["rolename"], PRINCIPALS) - self.assertEqual(len(policy), 1) - self.assertEqual(dict(policy), {"rolename": PRINCIPALS}) - - def test___delitem___hit(self): - policy = self._make_one() - policy._bindings["rolename"] = ["phred@example.com"] - del policy["rolename"] - self.assertEqual(len(policy), 0) - self.assertEqual(dict(policy), {}) - - def test___delitem___miss(self): - policy = self._make_one() - with self.assertRaises(KeyError): - del policy["nonesuch"] - - def test_owners_getter(self): - from google.cloud.iam import OWNER_ROLE - - MEMBER = "user:phred@example.com" - expected = frozenset([MEMBER]) - policy = self._make_one() - policy[OWNER_ROLE] = [MEMBER] - self.assertEqual(policy.owners, expected) - - def test_owners_setter(self): - import warnings - from google.cloud.iam import OWNER_ROLE - - MEMBER = "user:phred@example.com" - expected = set([MEMBER]) - policy = self._make_one() - with warnings.catch_warnings(): - warnings.simplefilter("always") - policy.owners = [MEMBER] - self.assertEqual(policy[OWNER_ROLE], expected) - - def test_editors_getter(self): - from google.cloud.iam import EDITOR_ROLE - - MEMBER = "user:phred@example.com" - expected = frozenset([MEMBER]) - policy = self._make_one() - policy[EDITOR_ROLE] = [MEMBER] - self.assertEqual(policy.editors, expected) - - def test_editors_setter(self): - import warnings - from google.cloud.iam import EDITOR_ROLE - - MEMBER = "user:phred@example.com" - expected = set([MEMBER]) - policy = self._make_one() - with warnings.catch_warnings(): - warnings.simplefilter("always") - policy.editors = [MEMBER] - self.assertEqual(policy[EDITOR_ROLE], expected) - - def test_viewers_getter(self): - from google.cloud.iam import VIEWER_ROLE - - MEMBER = "user:phred@example.com" - expected = frozenset([MEMBER]) - policy = self._make_one() - policy[VIEWER_ROLE] = [MEMBER] - self.assertEqual(policy.viewers, expected) - - def test_viewers_setter(self): - import warnings - from google.cloud.iam import VIEWER_ROLE - - MEMBER = "user:phred@example.com" - expected = set([MEMBER]) - policy = self._make_one() - with warnings.catch_warnings(): - warnings.simplefilter("always") - policy.viewers = [MEMBER] - self.assertEqual(policy[VIEWER_ROLE], expected) - - def test_user(self): - EMAIL = "phred@example.com" - MEMBER = "user:%s" % (EMAIL,) - policy = self._make_one() - self.assertEqual(policy.user(EMAIL), MEMBER) - - def test_service_account(self): - EMAIL = "phred@example.com" - MEMBER = "serviceAccount:%s" % (EMAIL,) - policy = self._make_one() - self.assertEqual(policy.service_account(EMAIL), MEMBER) - - def test_group(self): - EMAIL = "phred@example.com" - MEMBER = "group:%s" % (EMAIL,) - policy = self._make_one() - self.assertEqual(policy.group(EMAIL), MEMBER) - - def test_domain(self): - DOMAIN = "example.com" - MEMBER = "domain:%s" % (DOMAIN,) - policy = self._make_one() - self.assertEqual(policy.domain(DOMAIN), MEMBER) - - def test_all_users(self): - policy = self._make_one() - self.assertEqual(policy.all_users(), "allUsers") - - def test_authenticated_users(self): - policy = self._make_one() - self.assertEqual(policy.authenticated_users(), "allAuthenticatedUsers") - - def test_from_api_repr_only_etag(self): - empty = frozenset() - RESOURCE = {"etag": "ACAB"} - klass = self._get_target_class() - policy = klass.from_api_repr(RESOURCE) - self.assertEqual(policy.etag, "ACAB") - self.assertIsNone(policy.version) - self.assertEqual(policy.owners, empty) - self.assertEqual(policy.editors, empty) - self.assertEqual(policy.viewers, empty) - self.assertEqual(dict(policy), {}) - - def test_from_api_repr_complete(self): - from google.cloud.iam import OWNER_ROLE, EDITOR_ROLE, VIEWER_ROLE - - OWNER1 = "group:cloud-logs@google.com" - OWNER2 = "user:phred@example.com" - EDITOR1 = "domain:google.com" - EDITOR2 = "user:phred@example.com" - VIEWER1 = "serviceAccount:1234-abcdef@service.example.com" - VIEWER2 = "user:phred@example.com" - RESOURCE = { - "etag": "DEADBEEF", - "version": 17, - "bindings": [ - {"role": OWNER_ROLE, "members": [OWNER1, OWNER2]}, - {"role": EDITOR_ROLE, "members": [EDITOR1, EDITOR2]}, - {"role": VIEWER_ROLE, "members": [VIEWER1, VIEWER2]}, - ], - } - klass = self._get_target_class() - policy = klass.from_api_repr(RESOURCE) - self.assertEqual(policy.etag, "DEADBEEF") - self.assertEqual(policy.version, 17) - self.assertEqual(policy.owners, frozenset([OWNER1, OWNER2])) - self.assertEqual(policy.editors, frozenset([EDITOR1, EDITOR2])) - self.assertEqual(policy.viewers, frozenset([VIEWER1, VIEWER2])) - self.assertEqual( - dict(policy), - { - OWNER_ROLE: set([OWNER1, OWNER2]), - EDITOR_ROLE: set([EDITOR1, EDITOR2]), - VIEWER_ROLE: set([VIEWER1, VIEWER2]), - }, - ) - - def test_from_api_repr_unknown_role(self): - USER = "user:phred@example.com" - GROUP = "group:cloud-logs@google.com" - RESOURCE = { - "etag": "DEADBEEF", - "version": 17, - "bindings": [{"role": "unknown", "members": [USER, GROUP]}], - } - klass = self._get_target_class() - policy = klass.from_api_repr(RESOURCE) - self.assertEqual(policy.etag, "DEADBEEF") - self.assertEqual(policy.version, 17) - self.assertEqual(dict(policy), {"unknown": set([GROUP, USER])}) - - def test_to_api_repr_defaults(self): - policy = self._make_one() - self.assertEqual(policy.to_api_repr(), {}) - - def test_to_api_repr_only_etag(self): - policy = self._make_one("DEADBEEF") - self.assertEqual(policy.to_api_repr(), {"etag": "DEADBEEF"}) - - def test_to_api_repr_binding_wo_members(self): - policy = self._make_one() - policy["empty"] = [] - self.assertEqual(policy.to_api_repr(), {}) - - def test_to_api_repr_binding_w_duplicates(self): - from google.cloud.iam import OWNER_ROLE - - OWNER = "group:cloud-logs@google.com" - policy = self._make_one() - policy.owners = [OWNER, OWNER] - self.assertEqual( - policy.to_api_repr(), - {"bindings": [{"role": OWNER_ROLE, "members": [OWNER]}]}, - ) - - def test_to_api_repr_full(self): - import operator - from google.cloud.iam import OWNER_ROLE, EDITOR_ROLE, VIEWER_ROLE - - OWNER1 = "group:cloud-logs@google.com" - OWNER2 = "user:phred@example.com" - EDITOR1 = "domain:google.com" - EDITOR2 = "user:phred@example.com" - VIEWER1 = "serviceAccount:1234-abcdef@service.example.com" - VIEWER2 = "user:phred@example.com" - BINDINGS = [ - {"role": OWNER_ROLE, "members": [OWNER1, OWNER2]}, - {"role": EDITOR_ROLE, "members": [EDITOR1, EDITOR2]}, - {"role": VIEWER_ROLE, "members": [VIEWER1, VIEWER2]}, - ] - policy = self._make_one("DEADBEEF", 17) - policy.owners = [OWNER1, OWNER2] - policy.editors = [EDITOR1, EDITOR2] - policy.viewers = [VIEWER1, VIEWER2] - resource = policy.to_api_repr() - self.assertEqual(resource["etag"], "DEADBEEF") - self.assertEqual(resource["version"], 17) - key = operator.itemgetter("role") - self.assertEqual( - sorted(resource["bindings"], key=key), sorted(BINDINGS, key=key) - ) From 654f2d5e8320a7f9789a71c8ce616b2be23ed4c9 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Mon, 3 Dec 2018 14:37:38 -0800 Subject: [PATCH 2/5] remove iam doc file --- docs/core/iam.rst | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 docs/core/iam.rst diff --git a/docs/core/iam.rst b/docs/core/iam.rst deleted file mode 100644 index 3eb41fc72341..000000000000 --- a/docs/core/iam.rst +++ /dev/null @@ -1,6 +0,0 @@ -Identity and Access Management -============================== - -.. automodule:: google.cloud.iam - :members: - :show-inheritance: From dcf75805e693f2ae5244696209915419abb8be06 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Mon, 3 Dec 2018 14:45:39 -0800 Subject: [PATCH 3/5] remove iam from toctree --- docs/core/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/core/index.rst b/docs/core/index.rst index 5b44ddcb071d..e450a43c948a 100644 --- a/docs/core/index.rst +++ b/docs/core/index.rst @@ -10,7 +10,6 @@ Core retry timeout page_iterator - iam operation operations_client path_template From d8b3493ff85997a926d9faa9685d5440b9bddc6c Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Mon, 3 Dec 2018 15:10:09 -0800 Subject: [PATCH 4/5] point iam.rst to api_core --- docs/core/iam.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/core/iam.rst diff --git a/docs/core/iam.rst b/docs/core/iam.rst new file mode 100644 index 000000000000..053218489e07 --- /dev/null +++ b/docs/core/iam.rst @@ -0,0 +1,6 @@ +Identity and Access Management +============================== + +.. automodule:: google.api_core.iam + :members: + :show-inheritance: From 949761d2760e558b702f6f626520aba583563c39 Mon Sep 17 00:00:00 2001 From: Chris Wilcox Date: Mon, 3 Dec 2018 15:11:08 -0800 Subject: [PATCH 5/5] add iam to index --- docs/core/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/core/index.rst b/docs/core/index.rst index e450a43c948a..5b44ddcb071d 100644 --- a/docs/core/index.rst +++ b/docs/core/index.rst @@ -10,6 +10,7 @@ Core retry timeout page_iterator + iam operation operations_client path_template