diff --git a/iam/api-client/access.py b/iam/api-client/access.py new file mode 100644 index 00000000000..cd7930b4944 --- /dev/null +++ b/iam/api-client/access.py @@ -0,0 +1,132 @@ +# !/usr/bin/env python +# +# Copyright 2018 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. + +"""Demonstrates how to perform basic access management with Google Cloud IAM. + +For more information, see the documentation at +https://cloud.google.com/iam/docs/granting-changing-revoking-access. +""" + +import argparse +import os + +from google.oauth2 import service_account +import googleapiclient.discovery + + +credentials = service_account.Credentials.from_service_account_file( + filename=os.environ['GOOGLE_APPLICATION_CREDENTIALS'], + scopes=['https://www.googleapis.com/auth/cloud-platform']) +service = googleapiclient.discovery.build( + 'cloudresourcemanager', 'v1', credentials=credentials) + + +# [START iam_get_policy] +def get_policy(project_id): + """Gets IAM policy for a project.""" + + # pylint: disable=no-member + policy = service.projects().getIamPolicy( + resource=project_id, body={}).execute() + print(policy) + return policy +# [END iam_get_policy] + + +# [START iam_modify_policy_add_member] +def modify_policy_add_member(policy, role, member): + """Adds a new member to a role binding.""" + binding = next(b for b in policy['bindings'] if b['role'] == role) + binding['members'].append(member) + print(binding) + return policy +# [END iam_modify_policy_add_member] + + +# [START iam_modify_policy_add_role] +def modify_policy_add_role(policy, role, member): + """Adds a new role binding to a policy.""" + binding = { + 'role': role, + 'members': [member] + } + policy['bindings'].append(binding) + print(policy) + return policy +# [END iam_modify_policy_add_role] + + +# [START iam_set_policy] +def set_policy(project_id, policy): + """Sets IAM policy for a project.""" + + # pylint: disable=no-member + policy = service.projects().setIamPolicy( + resource=project_id, body={ + 'policy': policy + }).execute() + print(policy) + return policy +# [END iam_set_policy] + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + + subparsers = parser.add_subparsers(dest='command') + + # Get + get_parser = subparsers.add_parser( + 'get', help=get_policy.__doc__) + get_parser.add_argument('project_id') + + # Modify: add member + modify_member_parser = subparsers.add_parser( + 'modify_member', help=get_policy.__doc__) + modify_member_parser.add_argument('project_id') + modify_member_parser.add_argument('role') + modify_member_parser.add_argument('member') + + # Modify: add role + modify_role_parser = subparsers.add_parser( + 'modify_role', help=get_policy.__doc__) + modify_role_parser.add_argument('project_id') + modify_role_parser.add_argument('project_id') + modify_role_parser.add_argument('role') + modify_role_parser.add_argument('member') + + # Set + set_parser = subparsers.add_parser( + 'set', help=set_policy.__doc__) + set_parser.add_argument('project_id') + set_parser.add_argument('policy') + + args = parser.parse_args() + + if args.command == 'get': + get_policy(args.project_id) + elif args.command == 'set': + set_policy(args.project_id, args.policy) + elif args.command == 'add_member': + modify_policy_add_member(args.policy, args.role, args.member) + elif args.command == 'add_binding': + modify_policy_add_role(args.policy, args.role, args.member) + + +if __name__ == '__main__': + main() diff --git a/iam/api-client/access_test.py b/iam/api-client/access_test.py new file mode 100644 index 00000000000..eb95f9398ea --- /dev/null +++ b/iam/api-client/access_test.py @@ -0,0 +1,29 @@ +# Copyright 2018 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 os + +import access + + +def test_access(capsys): + project = os.environ['GCLOUD_PROJECT'] + + policy = access.get_policy(project) + out, _ = capsys.readouterr() + assert 'etag' in out + + policy = access.set_policy(project, policy) + out, _ = capsys.readouterr() + assert 'etag' in out diff --git a/iam/api-client/custom_roles.py b/iam/api-client/custom_roles.py new file mode 100644 index 00000000000..c4eed710bf7 --- /dev/null +++ b/iam/api-client/custom_roles.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python + +# Copyright 2018 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. + +"""Demonstrates how to perform basic operations with Google Cloud IAM +custom roles. + +For more information, see the documentation at +https://cloud.google.com/iam/docs/creating-custom-roles. +""" + +import argparse +import os + +from google.oauth2 import service_account +import googleapiclient.discovery + + +credentials = service_account.Credentials.from_service_account_file( + filename=os.environ['GOOGLE_APPLICATION_CREDENTIALS'], + scopes=['https://www.googleapis.com/auth/cloud-platform']) +service = googleapiclient.discovery.build( + 'iam', 'v1', credentials=credentials) + + +# [START iam_query_testable_permissions] +def query_testable_permissions(resource): + """Lists valid permissions for a resource.""" + + # pylint: disable=no-member + permissions = service.permissions().queryTestablePermissions(body={ + 'fullResourceName': resource + }).execute()['permissions'] + for p in permissions: + print(p['name']) +# [END iam_query_testable_permissions] + + +# [START iam_get_role] +def get_role(name): + """Gets a role.""" + + # pylint: disable=no-member + role = service.roles().get(name=name).execute() + print(role['name']) + for permission in role['includedPermissions']: + print(permission) +# [END iam_get_role] + + +# [START iam_create_role] +def create_role(name, project, title, description, permissions, stage): + """Creates a role.""" + + # pylint: disable=no-member + role = service.projects().roles().create( + parent='projects/' + project, + body={ + 'roleId': name, + 'role': { + 'title': title, + 'description': description, + 'includedPermissions': permissions, + 'stage': stage + } + }).execute() + + print('Created role: ' + role['name']) + return role +# [END iam_create_role] + + +# [START iam_edit_role] +def edit_role(name, project, title, description, permissions, stage): + """Creates a role.""" + + # pylint: disable=no-member + role = service.projects().roles().patch( + name='projects/' + project + '/roles/' + name, + body={ + 'title': title, + 'description': description, + 'includedPermissions': permissions, + 'stage': stage + }).execute() + + print('Updated role: ' + role['name']) + return role +# [END iam_edit_role] + + +# [START iam_list_roles] +def list_roles(project_id): + """Lists roles.""" + + # pylint: disable=no-member + roles = service.roles().list( + parent='projects/' + project_id).execute()['roles'] + for role in roles: + print(role['name']) +# [END iam_list_roles] + + +# [START iam_disable_role] +def disable_role(name, project): + """Disables a role.""" + + # pylint: disable=no-member + role = service.projects().roles().patch( + name='projects/' + project + '/roles/' + name, + body={ + 'stage': 'DISABLED' + }).execute() + + print('Disabled role: ' + role['name']) + return role +# [END iam_disable_role] + + +# [START iam_delete_role] +def delete_role(name, project): + """Deletes a role.""" + + # pylint: disable=no-member + role = service.projects().roles().delete( + name='projects/' + project + '/roles/' + name).execute() + + print('Deleted role: ' + name) + return role +# [END iam_delete_role] + + +# [START iam_undelete_role] +def undelete_role(name, project): + """Undeletes a role.""" + + # pylint: disable=no-member + role = service.projects().roles().patch( + name='projects/' + project + '/roles/' + name, + body={ + 'stage': 'DISABLED' + }).execute() + + print('Disabled role: ' + role['name']) + return role +# [END iam_undelete_role] + + +def main(): + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + + subparsers = parser.add_subparsers(dest='command') + + # Permissions + view_permissions_parser = subparsers.add_parser( + 'permissions', help=query_testable_permissions.__doc__) + view_permissions_parser.add_argument('resource') + + # Get + get_role_parser = subparsers.add_parser('get', help=get_role.__doc__) + get_role_parser.add_argument('name') + + # Create + get_role_parser = subparsers.add_parser('create', help=create_role.__doc__) + get_role_parser.add_argument('name') + get_role_parser.add_argument('project') + get_role_parser.add_argument('title') + get_role_parser.add_argument('description') + get_role_parser.add_argument('permissions') + get_role_parser.add_argument('stage') + + # Edit + edit_role_parser = subparsers.add_parser('edit', help=create_role.__doc__) + edit_role_parser.add_argument('name') + edit_role_parser.add_argument('project') + edit_role_parser.add_argument('title') + edit_role_parser.add_argument('description') + edit_role_parser.add_argument('permissions') + edit_role_parser.add_argument('stage') + + # List + list_roles_parser = subparsers.add_parser('list', help=list_roles.__doc__) + list_roles_parser.add_argument('project_id') + + # Disable + disable_role_parser = subparsers.add_parser( + 'disable', help=get_role.__doc__) + disable_role_parser.add_argument('name') + disable_role_parser.add_argument('project') + + # Delete + delete_role_parser = subparsers.add_parser('delete', help=get_role.__doc__) + delete_role_parser.add_argument('name') + delete_role_parser.add_argument('project') + + # Undelete + undelete_role_parser = subparsers.add_parser( + 'undelete', help=get_role.__doc__) + undelete_role_parser.add_argument('name') + undelete_role_parser.add_argument('project') + + args = parser.parse_args() + + if args.command == 'permissions': + query_testable_permissions(args.resource) + elif args.command == 'get': + get_role(args.name) + elif args.command == 'list': + list_roles(args.project_id) + elif args.command == 'create': + create_role( + args.name, args.project, args.title, + args.description, args.permissions, args.stage) + elif args.command == 'edit': + edit_role( + args.name, args.project, args.title, + args.description, args.permissions, args.stage) + elif args.command == 'disable': + disable_role(args.name, args.project) + elif args.command == 'delete': + delete_role(args.name, args.project) + elif args.command == 'undelete': + undelete_role(args.name, args.project) + + +if __name__ == '__main__': + main() diff --git a/iam/api-client/custom_roles_test.py b/iam/api-client/custom_roles_test.py new file mode 100644 index 00000000000..9617df86c15 --- /dev/null +++ b/iam/api-client/custom_roles_test.py @@ -0,0 +1,62 @@ +# Copyright 2016 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 os +import random + +import custom_roles + + +def test_custom_roles(capsys): + project = os.environ['GCLOUD_PROJECT'] + name = 'pythonTestCustomRole' + str(random.randint(0, 100000)) + title = 'Python Test Custom Role' + description = 'This is a Python test custom role.' + permissions = ['iam.roles.get'] + stage = 'GA' + + custom_roles.query_testable_permissions( + '//cloudresourcemanager.googleapis.com/projects/' + project + ) + out, _ = capsys.readouterr() + assert 'appengine' in out + + custom_roles.get_role('roles/appengine.appViewer') + out, _ = capsys.readouterr() + assert 'roles/' in out + + custom_roles.create_role( + name, project, title, description, permissions, stage) + out, _ = capsys.readouterr() + assert 'Created role:' in out + + custom_roles.edit_role(name, project, title, 'Updated', permissions, stage) + out, _ = capsys.readouterr() + assert 'Updated role:' in out + + custom_roles.list_roles(project) + out, _ = capsys.readouterr() + assert 'roles/' in out + + custom_roles.disable_role(name, project) + out, _ = capsys.readouterr() + assert 'Disabled role:' in out + + custom_roles.delete_role(name, project) + out, _ = capsys.readouterr() + assert 'Deleted role:' in out + + +if __name__ == '__main__': + test_custom_roles()