From 3fcf98f4dedf4132cda6a61ae3eb869243704d18 Mon Sep 17 00:00:00 2001 From: Ryan Matsumoto Date: Tue, 7 Feb 2017 13:15:52 -0800 Subject: [PATCH 01/52] Python KMS Apiary P1 samples [(#779)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/779) * Draft of first half of KMS samples * reversed wrong change * KMS Apiary Python samples - P1 * Few minor style issues * Adding back in space i accidentally deleted * Addressed all code review comments * Renamed api directory to api-client * Addressed more code review comments * Formatting change * Fix quickstart test Change-Id: Ib79dc1345c9c40547f3fd4e9c3c9a48963a3b399 * Update readme Change-Id: Icf4a66083f56d6f51be76ba1cf3b5dc8daf2c4c1 * Add readme Change-Id: I2fbaa55092ef8787f1423d499aa310cab258c0c1 * Added parsers * Final minor changes to parsers * Added autogenerated README * Changed snippets_test keyring name and cryptokey name --- samples/snippets/README.rst | 109 ++++++++ samples/snippets/README.rst.in | 20 ++ samples/snippets/quickstart.py | 48 ++++ samples/snippets/quickstart_test.py | 19 ++ samples/snippets/requirements.txt | 1 + samples/snippets/snippets.py | 371 ++++++++++++++++++++++++++++ samples/snippets/snippets_test.py | 147 +++++++++++ 7 files changed, 715 insertions(+) create mode 100644 samples/snippets/README.rst create mode 100644 samples/snippets/README.rst.in create mode 100644 samples/snippets/quickstart.py create mode 100644 samples/snippets/quickstart_test.py create mode 100644 samples/snippets/requirements.txt create mode 100644 samples/snippets/snippets.py create mode 100644 samples/snippets/snippets_test.py diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst new file mode 100644 index 00000000..4c660232 --- /dev/null +++ b/samples/snippets/README.rst @@ -0,0 +1,109 @@ +.. This file is automatically generated. Do not edit this file directly. + +Google Cloud KMS API Python Samples +=============================================================================== + +This directory contains samples for Google Cloud KMS API. The `Google Cloud KMS API`_ is a service that allows you to keep encryption keys centrally in the cloud, for direct use by cloud services. + + + + +.. _Google Cloud KMS API: https://cloud.google.com/kms/docs/ + +Setup +------------------------------------------------------------------------------- + + +Authentication +++++++++++++++ + +Authentication is typically done through `Application Default Credentials`_, +which means you do not have to change the code to authenticate as long as +your environment has credentials. You have a few options for setting up +authentication: + +#. When running locally, use the `Google Cloud SDK`_ + + .. code-block:: bash + + gcloud beta auth application-default login + + +#. When running on App Engine or Compute Engine, credentials are already + set-up. However, you may need to configure your Compute Engine instance + with `additional scopes`_. + +#. You can create a `Service Account key file`_. This file can be used to + authenticate to Google Cloud Platform services from any environment. To use + the file, set the ``GOOGLE_APPLICATION_CREDENTIALS`` environment variable to + the path to the key file, for example: + + .. code-block:: bash + + export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account.json + +.. _Application Default Credentials: https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow +.. _additional scopes: https://cloud.google.com/compute/docs/authentication#using +.. _Service Account key file: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount + +Install Dependencies +++++++++++++++++++++ + +#. Install `pip`_ and `virtualenv`_ if you do not already have them. + +#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. + + .. code-block:: bash + + $ virtualenv env + $ source env/bin/activate + +#. Install the dependencies needed to run the samples. + + .. code-block:: bash + + $ pip install -r requirements.txt + +.. _pip: https://pip.pypa.io/ +.. _virtualenv: https://virtualenv.pypa.io/ + +Samples +------------------------------------------------------------------------------- + +Quickstart ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + +To run this sample: + +.. code-block:: bash + + $ python quickstart.py + + +Snippets ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + +To run this sample: + +.. code-block:: bash + + $ python snippets.py + + usage: snippets.py [-h] + {create_keyring,create_cryptokey,encrypt,decrypt,disable_cryptokey_version,destroy_cryptokey_version,add_member_to_cryptokey_policy,get_keyring_policy} + ... + + positional arguments: + {create_keyring,create_cryptokey,encrypt,decrypt,disable_cryptokey_version,destroy_cryptokey_version,add_member_to_cryptokey_policy,get_keyring_policy} + + optional arguments: + -h, --help show this help message and exit + + + + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/samples/snippets/README.rst.in b/samples/snippets/README.rst.in new file mode 100644 index 00000000..9489051c --- /dev/null +++ b/samples/snippets/README.rst.in @@ -0,0 +1,20 @@ +# This file is used to generate README.rst + +product: + name: Google Cloud KMS API + short_name: Cloud KMS API + url: https://cloud.google.com/kms/docs/ + description: > + The `Google Cloud KMS API`_ is a service that allows you to keep encryption + keys centrally in the cloud, for direct use by cloud services. + +setup: +- auth +- install_deps + +samples: +- name: Quickstart + file: quickstart.py +- name: Snippets + file: snippets.py + show_help: True diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py new file mode 100644 index 00000000..4e94a669 --- /dev/null +++ b/samples/snippets/quickstart.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# Copyright 2017 Google, Inc +# +# 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 + + +def run_quickstart(): + # [START kms_quickstart] + # Imports the Google APIs client library + from googleapiclient import discovery + + # Your Google Cloud Platform project ID + project_id = 'YOUR_PROJECT_ID' + + # Lists keys in the "global" location. + location = 'global' + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # The resource name of the location associated with the key rings. + parent = 'projects/{}/locations/{}'.format(project_id, location) + + # Lists key rings + request = kms_client.projects().locations().keyRings().list(parent=parent) + response = request.execute() + + if 'keyRings' in response and response['keyRings']: + print('Key rings:') + for key_ring in response['keyRings']: + print(key_ring['name']) + else: + print('No key rings found.') + # [END kms_quickstart] + + +if __name__ == '__main__': + run_quickstart() diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py new file mode 100644 index 00000000..0db901e6 --- /dev/null +++ b/samples/snippets/quickstart_test.py @@ -0,0 +1,19 @@ +# Copyright 2017 Google Inc. 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. + + +def test_quickstart(api_client_inject_project_id): + import quickstart + + quickstart.run_quickstart() diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt new file mode 100644 index 00000000..ce6a9bf5 --- /dev/null +++ b/samples/snippets/requirements.txt @@ -0,0 +1 @@ +google-api-python-client==1.6.1 diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py new file mode 100644 index 00000000..9469b939 --- /dev/null +++ b/samples/snippets/snippets.py @@ -0,0 +1,371 @@ +#!/usr/bin/env python + +# Copyright 2017 Google, Inc +# +# 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 + +import argparse +import base64 +import io + +from googleapiclient import discovery + + +# [START kms_create_keyring] +def create_keyring(project_id, location, keyring): + """Creates a KeyRing in the given location (e.g. global).""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # The resource name of the location associated with the KeyRing. + parent = 'projects/{}/locations/{}'.format(project_id, location) + + # Create KeyRing + request = kms_client.projects().locations().keyRings().create( + parent=parent, body={}, keyRingId=keyring) + response = request.execute() + + print('Created KeyRing {}.'.format(response['name'])) +# [END kms_create_keyring] + + +# [START kms_create_cryptokey] +def create_cryptokey(project_id, location, keyring, cryptokey): + """Creates a CryptoKey within a KeyRing in the given location.""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # The resource name of the KeyRing associated with the CryptoKey. + parent = 'projects/{}/locations/{}/keyRings/{}'.format( + project_id, location, keyring) + + # Create a CryptoKey for the given KeyRing. + request = kms_client.projects().locations().keyRings().cryptoKeys().create( + parent=parent, body={'purpose': 'ENCRYPT_DECRYPT'}, + cryptoKeyId=cryptokey) + response = request.execute() + + print('Created CryptoKey {}.'.format(response['name'])) +# [END kms_create_cryptokey] + + +# [START kms_encrypt] +def encrypt(project_id, location, keyring, cryptokey, plaintext_file_name, + encrypted_file_name): + """Encrypts data from a plaintext_file_name using the provided CryptoKey + and saves it to an encrypted_file_name so it can only be recovered with a + call to decrypt.""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # The resource name of the CryptoKey. + name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( + project_id, location, keyring, cryptokey) + + # Read text from the input file. + with io.open(plaintext_file_name, 'rb') as plaintext_file: + plaintext = plaintext_file.read() + encoded_text = base64.b64encode(plaintext) + + # Use the KMS API to encrypt the text. + cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() + request = cryptokeys.encrypt( + name=name, body={'plaintext': encoded_text.decode('utf-8')}) + response = request.execute() + + # Write the encrypted text to a file. + with io.open(encrypted_file_name, 'wb') as encrypted_file: + encrypted_file.write(response['ciphertext'].encode('utf-8')) + + print('Saved encrypted text to {}.'.format(encrypted_file_name)) +# [END kms_encrypt] + + +# [START kms_decrypt] +def decrypt(project_id, location, keyring, cryptokey, encrypted_file_name, + decrypted_file_name): + """Decrypts data from encrypted_file_name that was previously encrypted + using the CryptoKey with a call to encrypt. Outputs decrypted data to + decrpyted_file_name.""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # The resource name of the CryptoKey. + name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( + project_id, location, keyring, cryptokey) + + # Read cipher text from the input file. + with io.open(encrypted_file_name, 'rb') as encrypted_file: + cipher_text = encrypted_file.read() + + # Use the KMS API to decrypt the text. + cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() + request = cryptokeys.decrypt( + name=name, body={'ciphertext': cipher_text.decode('utf-8')}) + response = request.execute() + + # Write the plain text to a file. + with io.open(decrypted_file_name, 'wb') as decrypted_file: + plaintext_encoded = response['plaintext'] + plaintext_decoded = base64.b64decode(plaintext_encoded) + decrypted_file.write(plaintext_decoded) + + print('Saved decrypted text to {}.'.format(decrypted_file_name)) +# [END kms_decrypt] + + +# [START kms_disable_cryptokey_version] +def disable_cryptokey_version(project_id, location, keyring, cryptokey, + version): + """Disables a CryptoKeyVersion associated with a given CryptoKey and + KeyRing.""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # Construct the resource name of the CryptoKeyVersion. + name = ( + 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}' + .format(project_id, location, keyring, cryptokey, version)) + + # Use the KMS API to disable the CryptoKeyVersion. + cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() + request = cryptokeys.cryptoKeyVersions().patch( + name=name, body={'state': 'DISABLED'}, updateMask='state') + response = request.execute() + + print('CryptoKeyVersion {}\'s state has been set to {}.'.format( + name, response['state'])) +# [END kms_disable_cryptokey_version] + + +# [START kms_destroy_cryptokey_version] +def destroy_cryptokey_version( + project_id, location, keyring, cryptokey, version): + """Schedules a CryptoKeyVersion associated with a given CryptoKey and + KeyRing for destruction 24 hours in the future.""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # Construct the resource name of the CryptoKeyVersion. + name = ( + 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}' + .format(project_id, location, keyring, cryptokey, version)) + + # Use the KMS API to schedule the CryptoKeyVersion for destruction. + cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() + request = cryptokeys.cryptoKeyVersions().destroy(name=name, body={}) + response = request.execute() + + print('CryptoKeyVersion {}\'s state has been set to {}.'.format( + name, response['state'])) +# [END kms_destroy_cryptokey_version] + + +# [START kms_add_member_to_cryptokey_policy] +def add_member_to_cryptokey_policy( + project_id, location, keyring, cryptokey, member, role): + """Adds a member with a given role to the Identity and Access Management + (IAM) policy for a given CryptoKey associated with a KeyRing.""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # The resource name of the CryptoKey. + parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( + project_id, location, keyring, cryptokey) + + # Get the current IAM policy and add the new member to it. + cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() + policy_request = cryptokeys.getIamPolicy(resource=parent) + policy_response = policy_request.execute() + bindings = [] + if 'bindings' in policy_response.keys(): + bindings = policy_response['bindings'] + members = [] + members.append(member) + new_binding = dict() + new_binding['role'] = role + new_binding['members'] = members + bindings.append(new_binding) + policy_response['bindings'] = bindings + + # Set the new IAM Policy. + cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() + request = cryptokeys.setIamPolicy( + resource=parent, body={'policy': policy_response}) + request.execute() + + print_msg = ( + 'Member {} added with role {} to policy for CryptoKey {} in KeyRing {}' + .format(member, role, cryptokey, keyring)) + print(print_msg) +# [END kms_add_member_to_cryptokey_policy] + + +# [START kms_get_keyring_policy] +def get_keyring_policy(project_id, location, keyring): + """Gets the Identity and Access Management (IAM) policy for a given KeyRing + and prints out roles and the members assigned to those roles.""" + + # Creates an API client for the KMS API. + kms_client = discovery.build('cloudkms', 'v1beta1') + + # The resource name of the KeyRing. + parent = 'projects/{}/locations/{}/keyRings/{}'.format( + project_id, location, keyring) + + # Get the current IAM policy. + request = kms_client.projects().locations().keyRings().getIamPolicy( + resource=parent) + response = request.execute() + + if 'bindings' in response.keys(): + print('Printing IAM policy for resource {}:'.format(parent)) + for binding in response['bindings']: + print('') + print('Role: {}'.format(binding['role'])) + print('Members:') + for member in binding['members']: + print(member) + print('') + else: + print('No roles found for resource {}.'.format(parent)) +# [END kms_get_keyring_policy] + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + subparsers = parser.add_subparsers(dest='command') + + create_keyring_parser = subparsers.add_parser('create_keyring') + create_keyring_parser.add_argument('project_id') + create_keyring_parser.add_argument('location') + create_keyring_parser.add_argument('keyring') + + create_cryptokey_parser = subparsers.add_parser('create_cryptokey') + create_cryptokey_parser.add_argument('project_id') + create_cryptokey_parser.add_argument('location') + create_cryptokey_parser.add_argument('keyring') + create_cryptokey_parser.add_argument('cryptokey') + + encrypt_parser = subparsers.add_parser('encrypt') + encrypt_parser.add_argument('project_id') + encrypt_parser.add_argument('location') + encrypt_parser.add_argument('keyring') + encrypt_parser.add_argument('cryptokey') + encrypt_parser.add_argument('infile') + encrypt_parser.add_argument('outfile') + + decrypt_parser = subparsers.add_parser('decrypt') + decrypt_parser.add_argument('project_id') + decrypt_parser.add_argument('location') + decrypt_parser.add_argument('keyring') + decrypt_parser.add_argument('cryptokey') + decrypt_parser.add_argument('infile') + decrypt_parser.add_argument('outfile') + + disable_cryptokey_version_parser = subparsers.add_parser( + 'disable_cryptokey_version') + disable_cryptokey_version_parser.add_argument('project_id') + disable_cryptokey_version_parser.add_argument('location') + disable_cryptokey_version_parser.add_argument('keyring') + disable_cryptokey_version_parser.add_argument('cryptokey') + disable_cryptokey_version_parser.add_argument('version') + + destroy_cryptokey_version_parser = subparsers.add_parser( + 'destroy_cryptokey_version') + destroy_cryptokey_version_parser.add_argument('project_id') + destroy_cryptokey_version_parser.add_argument('location') + destroy_cryptokey_version_parser.add_argument('keyring') + destroy_cryptokey_version_parser.add_argument('cryptokey') + destroy_cryptokey_version_parser.add_argument('version') + + add_member_to_cryptokey_policy_parser = subparsers.add_parser( + 'add_member_to_cryptokey_policy') + add_member_to_cryptokey_policy_parser.add_argument('project_id') + add_member_to_cryptokey_policy_parser.add_argument('location') + add_member_to_cryptokey_policy_parser.add_argument('keyring') + add_member_to_cryptokey_policy_parser.add_argument('cryptokey') + add_member_to_cryptokey_policy_parser.add_argument('member') + add_member_to_cryptokey_policy_parser.add_argument('role') + + get_keyring_policy_parser = subparsers.add_parser('get_keyring_policy') + get_keyring_policy_parser.add_argument('project_id') + get_keyring_policy_parser.add_argument('location') + get_keyring_policy_parser.add_argument('keyring') + + args = parser.parse_args() + + if args.command == 'create_keyring': + create_keyring( + args.project_id, + args.location, + args.keyring) + elif args.command == 'create_cryptokey': + create_cryptokey( + args.project_id, + args.location, + args.keyring, + args.cryptokey) + elif args.command == 'encrypt': + encrypt( + args.project_id, + args.location, + args.keyring, + args.cryptokey, + args.infile, + args.outfile) + elif args.command == 'decrypt': + decrypt( + args.project_id, + args.location, + args.keyring, + args.cryptokey, + args.infile, + args.outfile) + elif args.command == 'disable_cryptokey_version': + disable_cryptokey_version( + args.project_id, + args.location, + args.keyring, + args.cryptokey, + args.version) + elif args.command == 'destroy_cryptokey_version': + destroy_cryptokey_version( + args.project_id, + args.location, + args.keyring, + args.cryptokey, + args.version) + elif args.command == 'add_member_to_cryptokey_policy': + add_member_to_cryptokey_policy( + args.project_id, + args.location, + args.keyring, + args.cryptokey, + args.member, + args.role) + elif args.command == 'get_keyring_policy': + get_keyring_policy( + args.project_id, + args.location, + args.keyring) diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py new file mode 100644 index 00000000..e4deda1c --- /dev/null +++ b/samples/snippets/snippets_test.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python + +# Copyright 2017 Google, Inc +# +# 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 + +from googleapiclient import discovery + +import snippets + + +# Your Google Cloud Platform Key Location +LOCATION = 'global' + +# Your Google Cloud Platform KeyRing name +KEYRING = 'sample-keyring-43' + +# Your Google Cloud Platform CryptoKey name +CRYPTOKEY = 'sample-key-43' + +# Your Google Cloud Platform CryptoKeyVersion name +VERSION = 1 + +# A member to add to our IAM policy +MEMBER = 'user:ryanmats@google.com' + +# The role we want our new member to have for our IAM policy +ROLE = 'roles/owner' + + +def test_create_keyring(capsys, cloud_config): + snippets.create_keyring(cloud_config.project, LOCATION, KEYRING) + out, _ = capsys.readouterr() + expected = 'Created KeyRing projects/{}/locations/{}/keyRings/{}.'.format( + cloud_config.project, LOCATION, KEYRING) + assert expected in out + + +def test_create_cryptokey(capsys, cloud_config): + snippets.create_cryptokey( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY) + out, _ = capsys.readouterr() + expected = ( + 'Created CryptoKey projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}.' + .format(cloud_config.project, LOCATION, KEYRING, CRYPTOKEY)) + assert expected in out + + +def test_encrypt_decrypt(capsys, cloud_config, tmpdir): + # Write to a plaintext file. + tmpdir.join('in.txt').write('SampleText') + + # Construct temporary files. + plaintext_file = tmpdir.join('in.txt') + encrypted_file = tmpdir.join('out.txt') + decrypted_file = tmpdir.join('out2.txt') + + # Encrypt text and then decrypt it. + snippets.encrypt( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, + str(plaintext_file), str(encrypted_file)) + snippets.decrypt( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, + str(encrypted_file), str(decrypted_file)) + + # Make sure the decrypted text matches the original text. + decrypted_text = decrypted_file.read() + assert decrypted_text == 'SampleText' + + # Make sure other output is as expected. + out, _ = capsys.readouterr() + assert 'Saved encrypted text to {}.'.format(str(encrypted_file)) in out + assert 'Saved decrypted text to {}.'.format(str(decrypted_file)) in out + + +def test_disable_cryptokey_version(capsys, cloud_config): + snippets.disable_cryptokey_version( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION) + out, _ = capsys.readouterr() + expected = ( + 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}\'s state has been set to {}.' + .format( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION, + 'DISABLED')) + assert expected in out + + +def test_destroy_cryptokey_version(capsys, cloud_config): + snippets.destroy_cryptokey_version( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION) + out, _ = capsys.readouterr() + expected = ( + 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}\'s state has been set to {}.' + .format( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION, + 'DESTROY_SCHEDULED')) + assert expected in out + + +def test_add_member_to_cryptokey_policy(capsys, cloud_config): + snippets.add_member_to_cryptokey_policy( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, MEMBER, ROLE) + out, _ = capsys.readouterr() + expected = ( + 'Member {} added with role {} to policy for CryptoKey {} in KeyRing {}' + .format(MEMBER, ROLE, CRYPTOKEY, KEYRING)) + assert expected in out + + kms_client = discovery.build('cloudkms', 'v1beta1') + parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( + cloud_config.project, LOCATION, KEYRING, CRYPTOKEY) + cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() + policy_request = cryptokeys.getIamPolicy(resource=parent) + policy_response = policy_request.execute() + assert 'bindings' in policy_response.keys() + bindings = policy_response['bindings'] + found_member_role_pair = False + for binding in bindings: + if binding['role'] == ROLE: + for user in binding['members']: + if user == MEMBER: + found_member_role_pair = True + assert found_member_role_pair + + +def test_get_keyring_policy(capsys, cloud_config): + project_id = cloud_config.project + snippets.get_keyring_policy(project_id, LOCATION, KEYRING) + out, _ = capsys.readouterr() + expected_roles_exist = ( + 'Printing IAM policy for resource projects/{}/locations/{}/keyRings/{}' + ':'.format(project_id, LOCATION, KEYRING)) + expected_no_roles = ( + 'No roles found for resource projects/{}/locations/{}/keyRings/{}.' + .format(project_id, LOCATION, KEYRING)) + assert (expected_roles_exist in out) or (expected_no_roles in out) From 1f3c3b98adbb04522de7b0f4f47c647c9716e9e7 Mon Sep 17 00:00:00 2001 From: Ryan Matsumoto Date: Wed, 8 Feb 2017 14:41:11 -0800 Subject: [PATCH 02/52] random generation of keyring / cryptokey names [(#786)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/786) * random generation of keyring / cryptokey names * Fixed formatting of keyring name and cryptokey name --- samples/snippets/snippets_test.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index e4deda1c..2ea0b12c 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -13,6 +13,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and +import random +import string + from googleapiclient import discovery import snippets @@ -22,10 +25,12 @@ LOCATION = 'global' # Your Google Cloud Platform KeyRing name -KEYRING = 'sample-keyring-43' +KEYRING = ''.join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(12)) # Your Google Cloud Platform CryptoKey name -CRYPTOKEY = 'sample-key-43' +CRYPTOKEY = ''.join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(12)) # Your Google Cloud Platform CryptoKeyVersion name VERSION = 1 From d3c9a9b72b95eb2328c1477bd692c91e3aaea6d7 Mon Sep 17 00:00:00 2001 From: DPE bot Date: Thu, 9 Feb 2017 08:59:42 -0800 Subject: [PATCH 03/52] Auto-update dependencies. [(#790)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/790) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index ce6a9bf5..4f77d693 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1 +1 @@ -google-api-python-client==1.6.1 +google-api-python-client==1.6.2 From 2f0e38903645f1786d937bb93d388efd65f8ac6a Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Thu, 16 Feb 2017 17:07:45 -0800 Subject: [PATCH 04/52] Remove usage of GoogleCredentials [(#810)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/810) --- samples/snippets/quickstart.py | 4 ++-- samples/snippets/snippets.py | 18 +++++++++--------- samples/snippets/snippets_test.py | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 4e94a669..f4e4c7be 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -17,7 +17,7 @@ def run_quickstart(): # [START kms_quickstart] # Imports the Google APIs client library - from googleapiclient import discovery + import googleapiclient.discovery # Your Google Cloud Platform project ID project_id = 'YOUR_PROJECT_ID' @@ -26,7 +26,7 @@ def run_quickstart(): location = 'global' # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # The resource name of the location associated with the key rings. parent = 'projects/{}/locations/{}'.format(project_id, location) diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index 9469b939..1c0a5292 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -17,7 +17,7 @@ import base64 import io -from googleapiclient import discovery +import googleapiclient.discovery # [START kms_create_keyring] @@ -25,7 +25,7 @@ def create_keyring(project_id, location, keyring): """Creates a KeyRing in the given location (e.g. global).""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # The resource name of the location associated with the KeyRing. parent = 'projects/{}/locations/{}'.format(project_id, location) @@ -44,7 +44,7 @@ def create_cryptokey(project_id, location, keyring, cryptokey): """Creates a CryptoKey within a KeyRing in the given location.""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # The resource name of the KeyRing associated with the CryptoKey. parent = 'projects/{}/locations/{}/keyRings/{}'.format( @@ -68,7 +68,7 @@ def encrypt(project_id, location, keyring, cryptokey, plaintext_file_name, call to decrypt.""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # The resource name of the CryptoKey. name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( @@ -101,7 +101,7 @@ def decrypt(project_id, location, keyring, cryptokey, encrypted_file_name, decrpyted_file_name.""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # The resource name of the CryptoKey. name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( @@ -134,7 +134,7 @@ def disable_cryptokey_version(project_id, location, keyring, cryptokey, KeyRing.""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # Construct the resource name of the CryptoKeyVersion. name = ( @@ -160,7 +160,7 @@ def destroy_cryptokey_version( KeyRing for destruction 24 hours in the future.""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # Construct the resource name of the CryptoKeyVersion. name = ( @@ -185,7 +185,7 @@ def add_member_to_cryptokey_policy( (IAM) policy for a given CryptoKey associated with a KeyRing.""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # The resource name of the CryptoKey. parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( @@ -225,7 +225,7 @@ def get_keyring_policy(project_id, location, keyring): and prints out roles and the members assigned to those roles.""" # Creates an API client for the KMS API. - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') # The resource name of the KeyRing. parent = 'projects/{}/locations/{}/keyRings/{}'.format( diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index 2ea0b12c..dc532bce 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -16,7 +16,7 @@ import random import string -from googleapiclient import discovery +import googleapiclient.discovery import snippets @@ -122,7 +122,7 @@ def test_add_member_to_cryptokey_policy(capsys, cloud_config): .format(MEMBER, ROLE, CRYPTOKEY, KEYRING)) assert expected in out - kms_client = discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( cloud_config.project, LOCATION, KEYRING, CRYPTOKEY) cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() From 27611b368a0a63638dfa8ef24e5699590e0e4254 Mon Sep 17 00:00:00 2001 From: Phil Coakley Date: Thu, 16 Mar 2017 17:20:15 -0400 Subject: [PATCH 05/52] Updates Could-KMS sample code to use V1 libraries. [(#856)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/856) --- samples/snippets/quickstart.py | 2 +- samples/snippets/snippets.py | 16 ++++++++-------- samples/snippets/snippets_test.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index f4e4c7be..042bcae9 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -26,7 +26,7 @@ def run_quickstart(): location = 'global' # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the location associated with the key rings. parent = 'projects/{}/locations/{}'.format(project_id, location) diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index 1c0a5292..8196c584 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -25,7 +25,7 @@ def create_keyring(project_id, location, keyring): """Creates a KeyRing in the given location (e.g. global).""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the location associated with the KeyRing. parent = 'projects/{}/locations/{}'.format(project_id, location) @@ -44,7 +44,7 @@ def create_cryptokey(project_id, location, keyring, cryptokey): """Creates a CryptoKey within a KeyRing in the given location.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the KeyRing associated with the CryptoKey. parent = 'projects/{}/locations/{}/keyRings/{}'.format( @@ -68,7 +68,7 @@ def encrypt(project_id, location, keyring, cryptokey, plaintext_file_name, call to decrypt.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the CryptoKey. name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( @@ -101,7 +101,7 @@ def decrypt(project_id, location, keyring, cryptokey, encrypted_file_name, decrpyted_file_name.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the CryptoKey. name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( @@ -134,7 +134,7 @@ def disable_cryptokey_version(project_id, location, keyring, cryptokey, KeyRing.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # Construct the resource name of the CryptoKeyVersion. name = ( @@ -160,7 +160,7 @@ def destroy_cryptokey_version( KeyRing for destruction 24 hours in the future.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # Construct the resource name of the CryptoKeyVersion. name = ( @@ -185,7 +185,7 @@ def add_member_to_cryptokey_policy( (IAM) policy for a given CryptoKey associated with a KeyRing.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the CryptoKey. parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( @@ -225,7 +225,7 @@ def get_keyring_policy(project_id, location, keyring): and prints out roles and the members assigned to those roles.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the KeyRing. parent = 'projects/{}/locations/{}/keyRings/{}'.format( diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index dc532bce..873ce90d 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -122,7 +122,7 @@ def test_add_member_to_cryptokey_policy(capsys, cloud_config): .format(MEMBER, ROLE, CRYPTOKEY, KEYRING)) assert expected in out - kms_client = googleapiclient.discovery.build('cloudkms', 'v1beta1') + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( cloud_config.project, LOCATION, KEYRING, CRYPTOKEY) cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() From 57dbdb1ed6df0a604cb1e466223809571ebd2d64 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Tue, 4 Apr 2017 16:08:30 -0700 Subject: [PATCH 06/52] Remove cloud config fixture [(#887)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/887) * Remove cloud config fixture * Fix client secrets * Fix bigtable instance --- samples/snippets/snippets_test.py | 42 ++++++++++++++++--------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index 873ce90d..b3c4800d 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -13,6 +13,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and +import os import random import string @@ -20,6 +21,7 @@ import snippets +PROJECT = os.environ['GCLOUD_PROJECT'] # Your Google Cloud Platform Key Location LOCATION = 'global' @@ -42,25 +44,25 @@ ROLE = 'roles/owner' -def test_create_keyring(capsys, cloud_config): - snippets.create_keyring(cloud_config.project, LOCATION, KEYRING) +def test_create_keyring(capsys): + snippets.create_keyring(PROJECT, LOCATION, KEYRING) out, _ = capsys.readouterr() expected = 'Created KeyRing projects/{}/locations/{}/keyRings/{}.'.format( - cloud_config.project, LOCATION, KEYRING) + PROJECT, LOCATION, KEYRING) assert expected in out -def test_create_cryptokey(capsys, cloud_config): +def test_create_cryptokey(capsys): snippets.create_cryptokey( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY) + PROJECT, LOCATION, KEYRING, CRYPTOKEY) out, _ = capsys.readouterr() expected = ( 'Created CryptoKey projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}.' - .format(cloud_config.project, LOCATION, KEYRING, CRYPTOKEY)) + .format(PROJECT, LOCATION, KEYRING, CRYPTOKEY)) assert expected in out -def test_encrypt_decrypt(capsys, cloud_config, tmpdir): +def test_encrypt_decrypt(capsys, tmpdir): # Write to a plaintext file. tmpdir.join('in.txt').write('SampleText') @@ -71,10 +73,10 @@ def test_encrypt_decrypt(capsys, cloud_config, tmpdir): # Encrypt text and then decrypt it. snippets.encrypt( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, + PROJECT, LOCATION, KEYRING, CRYPTOKEY, str(plaintext_file), str(encrypted_file)) snippets.decrypt( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, + PROJECT, LOCATION, KEYRING, CRYPTOKEY, str(encrypted_file), str(decrypted_file)) # Make sure the decrypted text matches the original text. @@ -87,35 +89,35 @@ def test_encrypt_decrypt(capsys, cloud_config, tmpdir): assert 'Saved decrypted text to {}.'.format(str(decrypted_file)) in out -def test_disable_cryptokey_version(capsys, cloud_config): +def test_disable_cryptokey_version(capsys): snippets.disable_cryptokey_version( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION) + PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION) out, _ = capsys.readouterr() expected = ( 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' 'cryptoKeyVersions/{}\'s state has been set to {}.' .format( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION, + PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION, 'DISABLED')) assert expected in out -def test_destroy_cryptokey_version(capsys, cloud_config): +def test_destroy_cryptokey_version(capsys): snippets.destroy_cryptokey_version( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION) + PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION) out, _ = capsys.readouterr() expected = ( 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' 'cryptoKeyVersions/{}\'s state has been set to {}.' .format( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, VERSION, + PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION, 'DESTROY_SCHEDULED')) assert expected in out -def test_add_member_to_cryptokey_policy(capsys, cloud_config): +def test_add_member_to_cryptokey_policy(capsys): snippets.add_member_to_cryptokey_policy( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY, MEMBER, ROLE) + PROJECT, LOCATION, KEYRING, CRYPTOKEY, MEMBER, ROLE) out, _ = capsys.readouterr() expected = ( 'Member {} added with role {} to policy for CryptoKey {} in KeyRing {}' @@ -124,7 +126,7 @@ def test_add_member_to_cryptokey_policy(capsys, cloud_config): kms_client = googleapiclient.discovery.build('cloudkms', 'v1') parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - cloud_config.project, LOCATION, KEYRING, CRYPTOKEY) + PROJECT, LOCATION, KEYRING, CRYPTOKEY) cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() policy_request = cryptokeys.getIamPolicy(resource=parent) policy_response = policy_request.execute() @@ -139,8 +141,8 @@ def test_add_member_to_cryptokey_policy(capsys, cloud_config): assert found_member_role_pair -def test_get_keyring_policy(capsys, cloud_config): - project_id = cloud_config.project +def test_get_keyring_policy(capsys): + project_id = PROJECT snippets.get_keyring_policy(project_id, LOCATION, KEYRING) out, _ = capsys.readouterr() expected_roles_exist = ( From fa6ee74ccf4ee00704e5e1fd3e983bdaeeda6dbb Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Thu, 27 Apr 2017 09:54:41 -0700 Subject: [PATCH 07/52] Re-generate all readmes --- samples/snippets/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 4c660232..74dc229f 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -26,7 +26,7 @@ authentication: .. code-block:: bash - gcloud beta auth application-default login + gcloud auth application-default login #. When running on App Engine or Compute Engine, credentials are already From 966d0d92df89d100b2e98033b7e888e3cd07c4d7 Mon Sep 17 00:00:00 2001 From: Russ Amos Date: Wed, 16 Aug 2017 13:08:28 -0400 Subject: [PATCH 08/52] KMS: Clean up base64 logic in the encrypt and decrypt functions. [(#1074)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1074) The use of base64 is essentially an implementation detail of the Cloud KMS REST API: it is required only so that arbitrary binary data can be included in a JSON string, which only allows Unicode characters. Therefore, the "encrypt" sample function should decode the base64-encoded ciphertext before writing the file. Similarly, "decrypt" should not assume that an input file is base64-encoded, but should perform the base64-encoding itself before sending the encrypted data to KMS. This aligns with how the "gcloud kms encrypt" and "gcloud kms decrypt" commands behave. See https://stackoverflow.com/q/45699472 for an example of user confusion caused by the mismatch. --- samples/snippets/snippets.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index 8196c584..9400ffdf 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -77,17 +77,18 @@ def encrypt(project_id, location, keyring, cryptokey, plaintext_file_name, # Read text from the input file. with io.open(plaintext_file_name, 'rb') as plaintext_file: plaintext = plaintext_file.read() - encoded_text = base64.b64encode(plaintext) # Use the KMS API to encrypt the text. cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() request = cryptokeys.encrypt( - name=name, body={'plaintext': encoded_text.decode('utf-8')}) + name=name, + body={'plaintext': base64.b64encode(plaintext).decode('ascii')}) response = request.execute() + ciphertext = base64.b64decode(response['ciphertext'].encode('ascii')) # Write the encrypted text to a file. with io.open(encrypted_file_name, 'wb') as encrypted_file: - encrypted_file.write(response['ciphertext'].encode('utf-8')) + encrypted_file.write(ciphertext) print('Saved encrypted text to {}.'.format(encrypted_file_name)) # [END kms_encrypt] @@ -109,19 +110,19 @@ def decrypt(project_id, location, keyring, cryptokey, encrypted_file_name, # Read cipher text from the input file. with io.open(encrypted_file_name, 'rb') as encrypted_file: - cipher_text = encrypted_file.read() + ciphertext = encrypted_file.read() # Use the KMS API to decrypt the text. cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() request = cryptokeys.decrypt( - name=name, body={'ciphertext': cipher_text.decode('utf-8')}) + name=name, + body={'ciphertext': base64.b64encode(ciphertext).decode('ascii')}) response = request.execute() + plaintext = base64.b64decode(response['plaintext'].encode('ascii')) # Write the plain text to a file. with io.open(decrypted_file_name, 'wb') as decrypted_file: - plaintext_encoded = response['plaintext'] - plaintext_decoded = base64.b64decode(plaintext_encoded) - decrypted_file.write(plaintext_decoded) + decrypted_file.write(plaintext) print('Saved decrypted text to {}.'.format(decrypted_file_name)) # [END kms_decrypt] From 77e5c1afeabed47fc5d2344f5157650f8287c7f8 Mon Sep 17 00:00:00 2001 From: Russ Amos Date: Thu, 17 Aug 2017 12:10:28 -0400 Subject: [PATCH 09/52] KMS: Make the samples consistent with the other languages. [(#1075)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1075) In particular, this change: - changes to use a consistent "_id" suffix for resource name components - uses "data" instead of "text", since encryption is not restricted to text - substitutes "ciphertext" for "encrypted {text, data}" - spells "crypto key" and "key ring" each as two separate words, as in the API Tracking bug: http://b/64758639 --- samples/snippets/README.rst | 4 +- samples/snippets/snippets.py | 270 +++++++++++++++--------------- samples/snippets/snippets_test.py | 65 ++++--- 3 files changed, 170 insertions(+), 169 deletions(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 74dc229f..d1b495e8 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -94,11 +94,11 @@ To run this sample: $ python snippets.py usage: snippets.py [-h] - {create_keyring,create_cryptokey,encrypt,decrypt,disable_cryptokey_version,destroy_cryptokey_version,add_member_to_cryptokey_policy,get_keyring_policy} + {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,destroy_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} ... positional arguments: - {create_keyring,create_cryptokey,encrypt,decrypt,disable_cryptokey_version,destroy_cryptokey_version,add_member_to_cryptokey_policy,get_keyring_policy} + {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,destroy_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} optional arguments: -h, --help show this help message and exit diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index 9400ffdf..4f8ed56f 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -21,18 +21,18 @@ # [START kms_create_keyring] -def create_keyring(project_id, location, keyring): +def create_key_ring(project_id, location_id, key_ring_id): """Creates a KeyRing in the given location (e.g. global).""" # Creates an API client for the KMS API. kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the location associated with the KeyRing. - parent = 'projects/{}/locations/{}'.format(project_id, location) + parent = 'projects/{}/locations/{}'.format(project_id, location_id) # Create KeyRing request = kms_client.projects().locations().keyRings().create( - parent=parent, body={}, keyRingId=keyring) + parent=parent, body={}, keyRingId=key_ring_id) response = request.execute() print('Created KeyRing {}.'.format(response['name'])) @@ -40,7 +40,7 @@ def create_keyring(project_id, location, keyring): # [START kms_create_cryptokey] -def create_cryptokey(project_id, location, keyring, cryptokey): +def create_crypto_key(project_id, location_id, key_ring_id, crypto_key_id): """Creates a CryptoKey within a KeyRing in the given location.""" # Creates an API client for the KMS API. @@ -48,12 +48,12 @@ def create_cryptokey(project_id, location, keyring, cryptokey): # The resource name of the KeyRing associated with the CryptoKey. parent = 'projects/{}/locations/{}/keyRings/{}'.format( - project_id, location, keyring) + project_id, location_id, key_ring_id) # Create a CryptoKey for the given KeyRing. request = kms_client.projects().locations().keyRings().cryptoKeys().create( parent=parent, body={'purpose': 'ENCRYPT_DECRYPT'}, - cryptoKeyId=cryptokey) + cryptoKeyId=crypto_key_id) response = request.execute() print('Created CryptoKey {}.'.format(response['name'])) @@ -61,76 +61,76 @@ def create_cryptokey(project_id, location, keyring, cryptokey): # [START kms_encrypt] -def encrypt(project_id, location, keyring, cryptokey, plaintext_file_name, - encrypted_file_name): - """Encrypts data from a plaintext_file_name using the provided CryptoKey - and saves it to an encrypted_file_name so it can only be recovered with a - call to decrypt.""" +def encrypt(project_id, location_id, key_ring_id, crypto_key_id, + plaintext_file_name, ciphertext_file_name): + """Encrypts data from plaintext_file_name using the provided CryptoKey and + saves it to ciphertext_file_name so it can only be recovered with a call to + decrypt. + """ # Creates an API client for the KMS API. kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the CryptoKey. name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - project_id, location, keyring, cryptokey) + project_id, location_id, key_ring_id, crypto_key_id) - # Read text from the input file. + # Read data from the input file. with io.open(plaintext_file_name, 'rb') as plaintext_file: plaintext = plaintext_file.read() - # Use the KMS API to encrypt the text. - cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() - request = cryptokeys.encrypt( + # Use the KMS API to encrypt the data. + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + request = crypto_keys.encrypt( name=name, body={'plaintext': base64.b64encode(plaintext).decode('ascii')}) response = request.execute() ciphertext = base64.b64decode(response['ciphertext'].encode('ascii')) - # Write the encrypted text to a file. - with io.open(encrypted_file_name, 'wb') as encrypted_file: - encrypted_file.write(ciphertext) + # Write the encrypted data to a file. + with io.open(ciphertext_file_name, 'wb') as ciphertext_file: + ciphertext_file.write(ciphertext) - print('Saved encrypted text to {}.'.format(encrypted_file_name)) + print('Saved ciphertext to {}.'.format(ciphertext_file_name)) # [END kms_encrypt] # [START kms_decrypt] -def decrypt(project_id, location, keyring, cryptokey, encrypted_file_name, - decrypted_file_name): - """Decrypts data from encrypted_file_name that was previously encrypted - using the CryptoKey with a call to encrypt. Outputs decrypted data to - decrpyted_file_name.""" +def decrypt(project_id, location_id, key_ring_id, crypto_key_id, + ciphertext_file_name, plaintext_file_name): + """Decrypts data from ciphertext_file_name that was previously encrypted + using the provided CryptoKey and saves it to plaintext_file_name.""" # Creates an API client for the KMS API. kms_client = googleapiclient.discovery.build('cloudkms', 'v1') # The resource name of the CryptoKey. name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - project_id, location, keyring, cryptokey) + project_id, location_id, key_ring_id, crypto_key_id) - # Read cipher text from the input file. - with io.open(encrypted_file_name, 'rb') as encrypted_file: - ciphertext = encrypted_file.read() + # Read encrypted data from the input file. + with io.open(ciphertext_file_name, 'rb') as ciphertext_file: + ciphertext = ciphertext_file.read() - # Use the KMS API to decrypt the text. - cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() - request = cryptokeys.decrypt( + # Use the KMS API to decrypt the data. + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + request = crypto_keys.decrypt( name=name, body={'ciphertext': base64.b64encode(ciphertext).decode('ascii')}) response = request.execute() plaintext = base64.b64decode(response['plaintext'].encode('ascii')) - # Write the plain text to a file. - with io.open(decrypted_file_name, 'wb') as decrypted_file: - decrypted_file.write(plaintext) + # Write the decrypted data to a file. + with io.open(plaintext_file_name, 'wb') as plaintext_file: + plaintext_file.write(plaintext) - print('Saved decrypted text to {}.'.format(decrypted_file_name)) + print('Saved plaintext to {}.'.format(plaintext_file_name)) # [END kms_decrypt] # [START kms_disable_cryptokey_version] -def disable_cryptokey_version(project_id, location, keyring, cryptokey, - version): +def disable_crypto_key_version(project_id, location_id, key_ring_id, + crypto_key_id, version_id): """Disables a CryptoKeyVersion associated with a given CryptoKey and KeyRing.""" @@ -141,11 +141,12 @@ def disable_cryptokey_version(project_id, location, keyring, cryptokey, name = ( 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' 'cryptoKeyVersions/{}' - .format(project_id, location, keyring, cryptokey, version)) + .format( + project_id, location_id, key_ring_id, crypto_key_id, version_id)) # Use the KMS API to disable the CryptoKeyVersion. - cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() - request = cryptokeys.cryptoKeyVersions().patch( + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + request = crypto_keys.cryptoKeyVersions().patch( name=name, body={'state': 'DISABLED'}, updateMask='state') response = request.execute() @@ -155,8 +156,8 @@ def disable_cryptokey_version(project_id, location, keyring, cryptokey, # [START kms_destroy_cryptokey_version] -def destroy_cryptokey_version( - project_id, location, keyring, cryptokey, version): +def destroy_crypto_key_version( + project_id, location_id, key_ring_id, crypto_key_id, version_id): """Schedules a CryptoKeyVersion associated with a given CryptoKey and KeyRing for destruction 24 hours in the future.""" @@ -167,11 +168,12 @@ def destroy_cryptokey_version( name = ( 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' 'cryptoKeyVersions/{}' - .format(project_id, location, keyring, cryptokey, version)) + .format( + project_id, location_id, key_ring_id, crypto_key_id, version_id)) # Use the KMS API to schedule the CryptoKeyVersion for destruction. - cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() - request = cryptokeys.cryptoKeyVersions().destroy(name=name, body={}) + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + request = crypto_keys.cryptoKeyVersions().destroy(name=name, body={}) response = request.execute() print('CryptoKeyVersion {}\'s state has been set to {}.'.format( @@ -180,8 +182,8 @@ def destroy_cryptokey_version( # [START kms_add_member_to_cryptokey_policy] -def add_member_to_cryptokey_policy( - project_id, location, keyring, cryptokey, member, role): +def add_member_to_crypto_key_policy( + project_id, location_id, key_ring_id, crypto_key_id, member, role): """Adds a member with a given role to the Identity and Access Management (IAM) policy for a given CryptoKey associated with a KeyRing.""" @@ -190,11 +192,11 @@ def add_member_to_cryptokey_policy( # The resource name of the CryptoKey. parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - project_id, location, keyring, cryptokey) + project_id, location_id, key_ring_id, crypto_key_id) # Get the current IAM policy and add the new member to it. - cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() - policy_request = cryptokeys.getIamPolicy(resource=parent) + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + policy_request = crypto_keys.getIamPolicy(resource=parent) policy_response = policy_request.execute() bindings = [] if 'bindings' in policy_response.keys(): @@ -208,20 +210,20 @@ def add_member_to_cryptokey_policy( policy_response['bindings'] = bindings # Set the new IAM Policy. - cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() - request = cryptokeys.setIamPolicy( + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + request = crypto_keys.setIamPolicy( resource=parent, body={'policy': policy_response}) request.execute() print_msg = ( 'Member {} added with role {} to policy for CryptoKey {} in KeyRing {}' - .format(member, role, cryptokey, keyring)) + .format(member, role, crypto_key_id, key_ring_id)) print(print_msg) # [END kms_add_member_to_cryptokey_policy] # [START kms_get_keyring_policy] -def get_keyring_policy(project_id, location, keyring): +def get_key_ring_policy(project_id, location_id, key_ring_id): """Gets the Identity and Access Management (IAM) policy for a given KeyRing and prints out roles and the members assigned to those roles.""" @@ -230,7 +232,7 @@ def get_keyring_policy(project_id, location, keyring): # The resource name of the KeyRing. parent = 'projects/{}/locations/{}/keyRings/{}'.format( - project_id, location, keyring) + project_id, location_id, key_ring_id) # Get the current IAM policy. request = kms_client.projects().locations().keyRings().getIamPolicy( @@ -257,116 +259,116 @@ def get_keyring_policy(project_id, location, keyring): formatter_class=argparse.RawDescriptionHelpFormatter) subparsers = parser.add_subparsers(dest='command') - create_keyring_parser = subparsers.add_parser('create_keyring') - create_keyring_parser.add_argument('project_id') - create_keyring_parser.add_argument('location') - create_keyring_parser.add_argument('keyring') + create_key_ring_parser = subparsers.add_parser('create_key_ring') + create_key_ring_parser.add_argument('project') + create_key_ring_parser.add_argument('location') + create_key_ring_parser.add_argument('key_ring') - create_cryptokey_parser = subparsers.add_parser('create_cryptokey') - create_cryptokey_parser.add_argument('project_id') - create_cryptokey_parser.add_argument('location') - create_cryptokey_parser.add_argument('keyring') - create_cryptokey_parser.add_argument('cryptokey') + create_crypto_key_parser = subparsers.add_parser('create_crypto_key') + create_crypto_key_parser.add_argument('project') + create_crypto_key_parser.add_argument('location') + create_crypto_key_parser.add_argument('key_ring') + create_crypto_key_parser.add_argument('crypto_key') encrypt_parser = subparsers.add_parser('encrypt') - encrypt_parser.add_argument('project_id') + encrypt_parser.add_argument('project') encrypt_parser.add_argument('location') - encrypt_parser.add_argument('keyring') - encrypt_parser.add_argument('cryptokey') + encrypt_parser.add_argument('key_ring') + encrypt_parser.add_argument('crypto_key') encrypt_parser.add_argument('infile') encrypt_parser.add_argument('outfile') decrypt_parser = subparsers.add_parser('decrypt') - decrypt_parser.add_argument('project_id') + decrypt_parser.add_argument('project') decrypt_parser.add_argument('location') - decrypt_parser.add_argument('keyring') - decrypt_parser.add_argument('cryptokey') + decrypt_parser.add_argument('key_ring') + decrypt_parser.add_argument('crypto_key') decrypt_parser.add_argument('infile') decrypt_parser.add_argument('outfile') - disable_cryptokey_version_parser = subparsers.add_parser( - 'disable_cryptokey_version') - disable_cryptokey_version_parser.add_argument('project_id') - disable_cryptokey_version_parser.add_argument('location') - disable_cryptokey_version_parser.add_argument('keyring') - disable_cryptokey_version_parser.add_argument('cryptokey') - disable_cryptokey_version_parser.add_argument('version') - - destroy_cryptokey_version_parser = subparsers.add_parser( - 'destroy_cryptokey_version') - destroy_cryptokey_version_parser.add_argument('project_id') - destroy_cryptokey_version_parser.add_argument('location') - destroy_cryptokey_version_parser.add_argument('keyring') - destroy_cryptokey_version_parser.add_argument('cryptokey') - destroy_cryptokey_version_parser.add_argument('version') - - add_member_to_cryptokey_policy_parser = subparsers.add_parser( - 'add_member_to_cryptokey_policy') - add_member_to_cryptokey_policy_parser.add_argument('project_id') - add_member_to_cryptokey_policy_parser.add_argument('location') - add_member_to_cryptokey_policy_parser.add_argument('keyring') - add_member_to_cryptokey_policy_parser.add_argument('cryptokey') - add_member_to_cryptokey_policy_parser.add_argument('member') - add_member_to_cryptokey_policy_parser.add_argument('role') - - get_keyring_policy_parser = subparsers.add_parser('get_keyring_policy') - get_keyring_policy_parser.add_argument('project_id') - get_keyring_policy_parser.add_argument('location') - get_keyring_policy_parser.add_argument('keyring') + disable_crypto_key_version_parser = subparsers.add_parser( + 'disable_crypto_key_version') + disable_crypto_key_version_parser.add_argument('project') + disable_crypto_key_version_parser.add_argument('location') + disable_crypto_key_version_parser.add_argument('key_ring') + disable_crypto_key_version_parser.add_argument('crypto_key') + disable_crypto_key_version_parser.add_argument('version') + + destroy_crypto_key_version_parser = subparsers.add_parser( + 'destroy_crypto_key_version') + destroy_crypto_key_version_parser.add_argument('project') + destroy_crypto_key_version_parser.add_argument('location') + destroy_crypto_key_version_parser.add_argument('key_ring') + destroy_crypto_key_version_parser.add_argument('crypto_key') + destroy_crypto_key_version_parser.add_argument('version') + + add_member_to_crypto_key_policy_parser = subparsers.add_parser( + 'add_member_to_crypto_key_policy') + add_member_to_crypto_key_policy_parser.add_argument('project') + add_member_to_crypto_key_policy_parser.add_argument('location') + add_member_to_crypto_key_policy_parser.add_argument('key_ring') + add_member_to_crypto_key_policy_parser.add_argument('crypto_key') + add_member_to_crypto_key_policy_parser.add_argument('member') + add_member_to_crypto_key_policy_parser.add_argument('role') + + get_key_ring_policy_parser = subparsers.add_parser('get_key_ring_policy') + get_key_ring_policy_parser.add_argument('project') + get_key_ring_policy_parser.add_argument('location') + get_key_ring_policy_parser.add_argument('key_ring') args = parser.parse_args() - if args.command == 'create_keyring': - create_keyring( - args.project_id, + if args.command == 'create_key_ring': + create_key_ring( + args.project, args.location, - args.keyring) - elif args.command == 'create_cryptokey': - create_cryptokey( - args.project_id, + args.key_ring) + elif args.command == 'create_crypto_key': + create_crypto_key( + args.project, args.location, - args.keyring, - args.cryptokey) + args.key_ring, + args.crypto_key) elif args.command == 'encrypt': encrypt( - args.project_id, + args.project, args.location, - args.keyring, - args.cryptokey, + args.key_ring, + args.crypto_key, args.infile, args.outfile) elif args.command == 'decrypt': decrypt( - args.project_id, + args.project, args.location, - args.keyring, - args.cryptokey, + args.key_ring, + args.crypto_key, args.infile, args.outfile) - elif args.command == 'disable_cryptokey_version': - disable_cryptokey_version( - args.project_id, + elif args.command == 'disable_crypto_key_version': + disable_crypto_key_version( + args.project, args.location, - args.keyring, - args.cryptokey, + args.key_ring, + args.crypto_key, args.version) - elif args.command == 'destroy_cryptokey_version': - destroy_cryptokey_version( - args.project_id, + elif args.command == 'destroy_crypto_key_version': + destroy_crypto_key_version( + args.project, args.location, - args.keyring, - args.cryptokey, + args.key_ring, + args.crypto_key, args.version) - elif args.command == 'add_member_to_cryptokey_policy': - add_member_to_cryptokey_policy( - args.project_id, + elif args.command == 'add_member_to_crypto_key_policy': + add_member_to_crypto_key_policy( + args.project, args.location, - args.keyring, - args.cryptokey, + args.key_ring, + args.crypto_key, args.member, args.role) - elif args.command == 'get_keyring_policy': - get_keyring_policy( - args.project_id, + elif args.command == 'get_key_ring_policy': + get_key_ring_policy( + args.project, args.location, - args.keyring) + args.key_ring) diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index b3c4800d..1cfd7f21 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -27,11 +27,11 @@ LOCATION = 'global' # Your Google Cloud Platform KeyRing name -KEYRING = ''.join( +KEY_RING = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(12)) # Your Google Cloud Platform CryptoKey name -CRYPTOKEY = ''.join( +CRYPTO_KEY = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(12)) # Your Google Cloud Platform CryptoKeyVersion name @@ -44,21 +44,21 @@ ROLE = 'roles/owner' -def test_create_keyring(capsys): - snippets.create_keyring(PROJECT, LOCATION, KEYRING) +def test_create_key_ring(capsys): + snippets.create_key_ring(PROJECT, LOCATION, KEY_RING) out, _ = capsys.readouterr() expected = 'Created KeyRing projects/{}/locations/{}/keyRings/{}.'.format( - PROJECT, LOCATION, KEYRING) + PROJECT, LOCATION, KEY_RING) assert expected in out -def test_create_cryptokey(capsys): - snippets.create_cryptokey( - PROJECT, LOCATION, KEYRING, CRYPTOKEY) +def test_create_crypto_key(capsys): + snippets.create_crypto_key( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY) out, _ = capsys.readouterr() expected = ( 'Created CryptoKey projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}.' - .format(PROJECT, LOCATION, KEYRING, CRYPTOKEY)) + .format(PROJECT, LOCATION, KEY_RING, CRYPTO_KEY)) assert expected in out @@ -73,10 +73,10 @@ def test_encrypt_decrypt(capsys, tmpdir): # Encrypt text and then decrypt it. snippets.encrypt( - PROJECT, LOCATION, KEYRING, CRYPTOKEY, + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, str(plaintext_file), str(encrypted_file)) snippets.decrypt( - PROJECT, LOCATION, KEYRING, CRYPTOKEY, + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, str(encrypted_file), str(decrypted_file)) # Make sure the decrypted text matches the original text. @@ -85,50 +85,50 @@ def test_encrypt_decrypt(capsys, tmpdir): # Make sure other output is as expected. out, _ = capsys.readouterr() - assert 'Saved encrypted text to {}.'.format(str(encrypted_file)) in out - assert 'Saved decrypted text to {}.'.format(str(decrypted_file)) in out + assert 'Saved ciphertext to {}.'.format(str(encrypted_file)) in out + assert 'Saved plaintext to {}.'.format(str(decrypted_file)) in out -def test_disable_cryptokey_version(capsys): - snippets.disable_cryptokey_version( - PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION) +def test_disable_crypto_key_version(capsys): + snippets.disable_crypto_key_version( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) out, _ = capsys.readouterr() expected = ( 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' 'cryptoKeyVersions/{}\'s state has been set to {}.' .format( - PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION, + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, 'DISABLED')) assert expected in out -def test_destroy_cryptokey_version(capsys): - snippets.destroy_cryptokey_version( - PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION) +def test_destroy_crypto_key_version(capsys): + snippets.destroy_crypto_key_version( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) out, _ = capsys.readouterr() expected = ( 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' 'cryptoKeyVersions/{}\'s state has been set to {}.' .format( - PROJECT, LOCATION, KEYRING, CRYPTOKEY, VERSION, + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, 'DESTROY_SCHEDULED')) assert expected in out -def test_add_member_to_cryptokey_policy(capsys): - snippets.add_member_to_cryptokey_policy( - PROJECT, LOCATION, KEYRING, CRYPTOKEY, MEMBER, ROLE) +def test_add_member_to_crypto_key_policy(capsys): + snippets.add_member_to_crypto_key_policy( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, MEMBER, ROLE) out, _ = capsys.readouterr() expected = ( 'Member {} added with role {} to policy for CryptoKey {} in KeyRing {}' - .format(MEMBER, ROLE, CRYPTOKEY, KEYRING)) + .format(MEMBER, ROLE, CRYPTO_KEY, KEY_RING)) assert expected in out kms_client = googleapiclient.discovery.build('cloudkms', 'v1') parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - PROJECT, LOCATION, KEYRING, CRYPTOKEY) - cryptokeys = kms_client.projects().locations().keyRings().cryptoKeys() - policy_request = cryptokeys.getIamPolicy(resource=parent) + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY) + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + policy_request = crypto_keys.getIamPolicy(resource=parent) policy_response = policy_request.execute() assert 'bindings' in policy_response.keys() bindings = policy_response['bindings'] @@ -141,14 +141,13 @@ def test_add_member_to_cryptokey_policy(capsys): assert found_member_role_pair -def test_get_keyring_policy(capsys): - project_id = PROJECT - snippets.get_keyring_policy(project_id, LOCATION, KEYRING) +def test_get_key_ring_policy(capsys): + snippets.get_key_ring_policy(PROJECT, LOCATION, KEY_RING) out, _ = capsys.readouterr() expected_roles_exist = ( 'Printing IAM policy for resource projects/{}/locations/{}/keyRings/{}' - ':'.format(project_id, LOCATION, KEYRING)) + ':'.format(PROJECT, LOCATION, KEY_RING)) expected_no_roles = ( 'No roles found for resource projects/{}/locations/{}/keyRings/{}.' - .format(project_id, LOCATION, KEYRING)) + .format(PROJECT, LOCATION, KEY_RING)) assert (expected_roles_exist in out) or (expected_no_roles in out) From aa49dbc5013892b1e26093f19d46a4d253af74dd Mon Sep 17 00:00:00 2001 From: DPE bot Date: Wed, 30 Aug 2017 10:15:58 -0700 Subject: [PATCH 10/52] Auto-update dependencies. [(#1094)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1094) * Auto-update dependencies. * Relax assertions in the ocr_nl sample Change-Id: I6d37e5846a8d6dd52429cb30d501f448c52cbba1 * Drop unused logging apiary samples Change-Id: I545718283773cb729a5e0def8a76ebfa40829d51 --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 4f77d693..28ef8912 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1 +1 @@ -google-api-python-client==1.6.2 +google-api-python-client==1.6.3 From 04e00af5467016747f89e7c8c0b6252779790a63 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Mon, 18 Sep 2017 11:04:05 -0700 Subject: [PATCH 11/52] Update all generated readme auth instructions [(#1121)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1121) Change-Id: I03b5eaef8b17ac3dc3c0339fd2c7447bd3e11bd2 --- samples/snippets/README.rst | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index d1b495e8..8e264852 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -17,34 +17,12 @@ Setup Authentication ++++++++++++++ -Authentication is typically done through `Application Default Credentials`_, -which means you do not have to change the code to authenticate as long as -your environment has credentials. You have a few options for setting up -authentication: +This sample requires you to have authentication setup. Refer to the +`Authentication Getting Started Guide`_ for instructions on setting up +credentials for applications. -#. When running locally, use the `Google Cloud SDK`_ - - .. code-block:: bash - - gcloud auth application-default login - - -#. When running on App Engine or Compute Engine, credentials are already - set-up. However, you may need to configure your Compute Engine instance - with `additional scopes`_. - -#. You can create a `Service Account key file`_. This file can be used to - authenticate to Google Cloud Platform services from any environment. To use - the file, set the ``GOOGLE_APPLICATION_CREDENTIALS`` environment variable to - the path to the key file, for example: - - .. code-block:: bash - - export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account.json - -.. _Application Default Credentials: https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow -.. _additional scopes: https://cloud.google.com/compute/docs/authentication#using -.. _Service Account key file: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount +.. _Authentication Getting Started Guide: + https://cloud.google.com/docs/authentication/getting-started Install Dependencies ++++++++++++++++++++ From 5156f09caa7a560806b8500ff16a8d5b7022b21a Mon Sep 17 00:00:00 2001 From: DPE bot Date: Thu, 21 Sep 2017 13:40:34 -0700 Subject: [PATCH 12/52] Auto-update dependencies. [(#1133)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1133) * Auto-update dependencies. * Fix missing http library Change-Id: I99faa600f2f3f1f50f57694fc9835d7f35bda250 --- samples/snippets/requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 28ef8912..af5ec814 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1 +1,3 @@ -google-api-python-client==1.6.3 +google-api-python-client==1.6.4 +google-auth==1.1.1 +google-auth-httplib2==0.0.2 From f894679ee3e8e1a79a09ee7aebf3089b5ca2922e Mon Sep 17 00:00:00 2001 From: michaelawyu Date: Thu, 12 Oct 2017 10:16:11 -0700 Subject: [PATCH 13/52] Added Link to Python Setup Guide [(#1158)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1158) * Update Readme.rst to add Python setup guide As requested in b/64770713. This sample is linked in documentation https://cloud.google.com/bigtable/docs/scaling, and it would make more sense to update the guide here than in the documentation. * Update README.rst * Update README.rst * Update README.rst * Update README.rst * Update README.rst * Update install_deps.tmpl.rst * Updated readmegen scripts and re-generated related README files * Fixed the lint error --- samples/snippets/README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 8e264852..410fa8da 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -27,7 +27,10 @@ credentials for applications. Install Dependencies ++++++++++++++++++++ -#. Install `pip`_ and `virtualenv`_ if you do not already have them. +#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. + + .. _Python Development Environment Setup Guide: + https://cloud.google.com/python/setup #. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. From 98fa8151d1793a8e7a1349940b73af822571e02e Mon Sep 17 00:00:00 2001 From: DPE bot Date: Wed, 1 Nov 2017 12:30:10 -0700 Subject: [PATCH 14/52] Auto-update dependencies. [(#1186)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1186) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index af5ec814..558e42c2 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ google-api-python-client==1.6.4 -google-auth==1.1.1 +google-auth==1.2.0 google-auth-httplib2==0.0.2 From 091dbc6e2804b5de096c0a0ec81a2e058649c0f0 Mon Sep 17 00:00:00 2001 From: Walter Poupore Date: Tue, 7 Nov 2017 15:11:55 -0800 Subject: [PATCH 15/52] Adds snippets for enabling and restoring a key version [(#1196)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1196) * Adds snippets for enabling and restoring a key version * Fixed lint issues --- samples/snippets/snippets.py | 82 +++++++++++++++++++++++++++++++ samples/snippets/snippets_test.py | 26 ++++++++++ 2 files changed, 108 insertions(+) diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index 4f8ed56f..73e4a65d 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -155,6 +155,33 @@ def disable_crypto_key_version(project_id, location_id, key_ring_id, # [END kms_disable_cryptokey_version] +# [START kms_enable_cryptokey_version] +def enable_crypto_key_version(project_id, location_id, key_ring_id, + crypto_key_id, version_id): + """Enables a CryptoKeyVersion associated with a given CryptoKey and + KeyRing.""" + + # Creates an API client for the KMS API. + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + + # Construct the resource name of the CryptoKeyVersion. + name = ( + 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}' + .format( + project_id, location_id, key_ring_id, crypto_key_id, version_id)) + + # Use the KMS API to enable the CryptoKeyVersion. + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + request = crypto_keys.cryptoKeyVersions().patch( + name=name, body={'state': 'ENABLED'}, updateMask='state') + response = request.execute() + + print('CryptoKeyVersion {}\'s state has been set to {}.'.format( + name, response['state'])) +# [END kms_enable_cryptokey_version] + + # [START kms_destroy_cryptokey_version] def destroy_crypto_key_version( project_id, location_id, key_ring_id, crypto_key_id, version_id): @@ -181,6 +208,31 @@ def destroy_crypto_key_version( # [END kms_destroy_cryptokey_version] +# [START kms_restore_cryptokey_version] +def restore_crypto_key_version( + project_id, location_id, key_ring_id, crypto_key_id, version_id): + """Restores a CryptoKeyVersion that is scheduled for destruction.""" + + # Creates an API client for the KMS API. + kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + + # Construct the resource name of the CryptoKeyVersion. + name = ( + 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}' + .format( + project_id, location_id, key_ring_id, crypto_key_id, version_id)) + + # Use the KMS API to restore the CryptoKeyVersion. + crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() + request = crypto_keys.cryptoKeyVersions().restore(name=name, body={}) + response = request.execute() + + print('CryptoKeyVersion {}\'s state has been set to {}.'.format( + name, response['state'])) +# [END kms_restore_cryptokey_version] + + # [START kms_add_member_to_cryptokey_policy] def add_member_to_crypto_key_policy( project_id, location_id, key_ring_id, crypto_key_id, member, role): @@ -294,6 +346,14 @@ def get_key_ring_policy(project_id, location_id, key_ring_id): disable_crypto_key_version_parser.add_argument('crypto_key') disable_crypto_key_version_parser.add_argument('version') + enable_crypto_key_version_parser = subparsers.add_parser( + 'enable_crypto_key_version') + enable_crypto_key_version_parser.add_argument('project') + enable_crypto_key_version_parser.add_argument('location') + enable_crypto_key_version_parser.add_argument('key_ring') + enable_crypto_key_version_parser.add_argument('crypto_key') + enable_crypto_key_version_parser.add_argument('version') + destroy_crypto_key_version_parser = subparsers.add_parser( 'destroy_crypto_key_version') destroy_crypto_key_version_parser.add_argument('project') @@ -302,6 +362,14 @@ def get_key_ring_policy(project_id, location_id, key_ring_id): destroy_crypto_key_version_parser.add_argument('crypto_key') destroy_crypto_key_version_parser.add_argument('version') + restore_crypto_key_version_parser = subparsers.add_parser( + 'restore_crypto_key_version') + restore_crypto_key_version_parser.add_argument('project') + restore_crypto_key_version_parser.add_argument('location') + restore_crypto_key_version_parser.add_argument('key_ring') + restore_crypto_key_version_parser.add_argument('crypto_key') + restore_crypto_key_version_parser.add_argument('version') + add_member_to_crypto_key_policy_parser = subparsers.add_parser( 'add_member_to_crypto_key_policy') add_member_to_crypto_key_policy_parser.add_argument('project') @@ -352,6 +420,13 @@ def get_key_ring_policy(project_id, location_id, key_ring_id): args.key_ring, args.crypto_key, args.version) + elif args.command == 'enable_crypto_key_version': + enable_crypto_key_version( + args.project, + args.location, + args.key_ring, + args.crypto_key, + args.version) elif args.command == 'destroy_crypto_key_version': destroy_crypto_key_version( args.project, @@ -359,6 +434,13 @@ def get_key_ring_policy(project_id, location_id, key_ring_id): args.key_ring, args.crypto_key, args.version) + elif args.command == 'restore_crypto_key_version': + restore_crypto_key_version( + args.project, + args.location, + args.key_ring, + args.crypto_key, + args.version) elif args.command == 'add_member_to_crypto_key_policy': add_member_to_crypto_key_policy( args.project, diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index 1cfd7f21..b36d9644 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -102,6 +102,19 @@ def test_disable_crypto_key_version(capsys): assert expected in out +def test_enable_crypto_key_version(capsys): + snippets.enable_crypto_key_version( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) + out, _ = capsys.readouterr() + expected = ( + 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}\'s state has been set to {}.' + .format( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, + 'ENABLED')) + assert expected in out + + def test_destroy_crypto_key_version(capsys): snippets.destroy_crypto_key_version( PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) @@ -115,6 +128,19 @@ def test_destroy_crypto_key_version(capsys): assert expected in out +def test_restore_crypto_key_version(capsys): + snippets.restore_crypto_key_version( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) + out, _ = capsys.readouterr() + expected = ( + 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' + 'cryptoKeyVersions/{}\'s state has been set to {}.' + .format( + PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, + 'DISABLED')) + assert expected in out + + def test_add_member_to_crypto_key_policy(capsys): snippets.add_member_to_crypto_key_policy( PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, MEMBER, ROLE) From ff973d877b5d16a58bbbe1297772ee3309617152 Mon Sep 17 00:00:00 2001 From: DPE bot Date: Wed, 15 Nov 2017 12:18:33 -0800 Subject: [PATCH 16/52] Auto-update dependencies. [(#1217)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1217) --- samples/snippets/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 558e42c2..edd6472f 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ google-api-python-client==1.6.4 -google-auth==1.2.0 -google-auth-httplib2==0.0.2 +google-auth==1.2.1 +google-auth-httplib2==0.0.3 From b9a4c34162dd7474a5574a034c6396098e97b3a5 Mon Sep 17 00:00:00 2001 From: michaelawyu Date: Thu, 7 Dec 2017 10:34:29 -0800 Subject: [PATCH 17/52] Added "Open in Cloud Shell" buttons to README files [(#1254)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1254) --- samples/snippets/README.rst | 21 +++++++++++++++++---- samples/snippets/README.rst.in | 2 ++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 410fa8da..8fdace9c 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -3,6 +3,10 @@ Google Cloud KMS API Python Samples =============================================================================== +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/README.rst + + This directory contains samples for Google Cloud KMS API. The `Google Cloud KMS API`_ is a service that allows you to keep encryption keys centrally in the cloud, for direct use by cloud services. @@ -54,6 +58,10 @@ Samples Quickstart +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/quickstart.py;kms/api-client/README.rst + + To run this sample: @@ -66,6 +74,10 @@ To run this sample: Snippets +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/snippets.py;kms/api-client/README.rst + + To run this sample: @@ -75,16 +87,17 @@ To run this sample: $ python snippets.py usage: snippets.py [-h] - {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,destroy_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} + {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,enable_crypto_key_version,destroy_crypto_key_version,restore_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} ... - + positional arguments: - {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,destroy_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} - + {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,enable_crypto_key_version,destroy_crypto_key_version,restore_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} + optional arguments: -h, --help show this help message and exit + .. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/samples/snippets/README.rst.in b/samples/snippets/README.rst.in index 9489051c..6299b5a0 100644 --- a/samples/snippets/README.rst.in +++ b/samples/snippets/README.rst.in @@ -18,3 +18,5 @@ samples: - name: Snippets file: snippets.py show_help: True + +folder: kms/api-client \ No newline at end of file From 251d7960910dca9130cd1b236224992c3d213448 Mon Sep 17 00:00:00 2001 From: DPE bot Date: Wed, 10 Jan 2018 09:07:00 -0800 Subject: [PATCH 18/52] Auto-update dependencies. [(#1309)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1309) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index edd6472f..8bb83f80 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ google-api-python-client==1.6.4 -google-auth==1.2.1 +google-auth==1.3.0 google-auth-httplib2==0.0.3 From 091bcac4e359c2eb53278954477311ac20a17dbf Mon Sep 17 00:00:00 2001 From: DPE bot Date: Thu, 1 Feb 2018 22:20:35 -0800 Subject: [PATCH 19/52] Auto-update dependencies. [(#1320)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1320) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 8bb83f80..4bafec30 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ -google-api-python-client==1.6.4 +google-api-python-client==1.6.5 google-auth==1.3.0 google-auth-httplib2==0.0.3 From c4f16b4a85ff041b569d3def144b5d39d4f2800b Mon Sep 17 00:00:00 2001 From: DPE bot Date: Fri, 9 Feb 2018 10:46:48 -0800 Subject: [PATCH 20/52] Auto-update dependencies. [(#1355)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1355) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 4bafec30..35784043 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ google-api-python-client==1.6.5 -google-auth==1.3.0 +google-auth==1.4.0 google-auth-httplib2==0.0.3 From 4f3ccbf859519e66c3b609b0d12f9ef3eab29f24 Mon Sep 17 00:00:00 2001 From: DPE bot Date: Mon, 26 Feb 2018 09:03:37 -0800 Subject: [PATCH 21/52] Auto-update dependencies. [(#1359)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1359) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 35784043..500e732f 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ google-api-python-client==1.6.5 -google-auth==1.4.0 +google-auth==1.4.1 google-auth-httplib2==0.0.3 From 7264e6ab979c1aed2906a2f2d9df6522c17216f7 Mon Sep 17 00:00:00 2001 From: DPE bot Date: Mon, 2 Apr 2018 02:51:10 -0700 Subject: [PATCH 22/52] Auto-update dependencies. --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 500e732f..e5f3a6c5 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,3 @@ -google-api-python-client==1.6.5 +google-api-python-client==1.6.6 google-auth==1.4.1 google-auth-httplib2==0.0.3 From d1405160447c4cbbefef7bd2dc9ef6cc7dade4eb Mon Sep 17 00:00:00 2001 From: chenyumic Date: Fri, 6 Apr 2018 22:57:36 -0700 Subject: [PATCH 23/52] Regenerate the README files and fix the Open in Cloud Shell link for some samples [(#1441)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1441) --- samples/snippets/README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 8fdace9c..9bf0471b 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -12,7 +12,7 @@ This directory contains samples for Google Cloud KMS API. The `Google Cloud KMS -.. _Google Cloud KMS API: https://cloud.google.com/kms/docs/ +.. _Google Cloud KMS API: https://cloud.google.com/kms/docs/ Setup ------------------------------------------------------------------------------- @@ -59,7 +59,7 @@ Quickstart +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/quickstart.py;kms/api-client/README.rst + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/quickstart.py,kms/api-client/README.rst @@ -75,7 +75,7 @@ Snippets +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/snippets.py;kms/api-client/README.rst + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/snippets.py,kms/api-client/README.rst From 2857b8da179f9b13b86ca99f5bc44e5a9f0bad1e Mon Sep 17 00:00:00 2001 From: Frank Natividad Date: Thu, 26 Apr 2018 10:26:41 -0700 Subject: [PATCH 24/52] Update READMEs to fix numbering and add git clone [(#1464)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1464) --- samples/snippets/README.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 9bf0471b..28777c84 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -31,10 +31,16 @@ credentials for applications. Install Dependencies ++++++++++++++++++++ +#. Clone python-docs-samples and change directory to the sample directory you want to use. + + .. code-block:: bash + + $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git + #. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. - .. _Python Development Environment Setup Guide: - https://cloud.google.com/python/setup + .. _Python Development Environment Setup Guide: + https://cloud.google.com/python/setup #. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. From e2c063712791c072da3e05adb72190c0f9d72288 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 17 Aug 2018 09:44:26 -0700 Subject: [PATCH 25/52] added kms asymmetric samples [(#1638)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1638) --- samples/snippets/asymmetric.py | 136 +++++++++++++++++++++++++++ samples/snippets/asymmetric_test.py | 141 ++++++++++++++++++++++++++++ samples/snippets/requirements.txt | 1 + 3 files changed, 278 insertions(+) create mode 100644 samples/snippets/asymmetric.py create mode 100644 samples/snippets/asymmetric_test.py diff --git a/samples/snippets/asymmetric.py b/samples/snippets/asymmetric.py new file mode 100644 index 00000000..4127148f --- /dev/null +++ b/samples/snippets/asymmetric.py @@ -0,0 +1,136 @@ +#!/bin/python +# Copyright 2018 Google Inc. +# +# 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.rom googleapiclient import discovery + +import base64 +import hashlib + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, padding, utils + + +# [START kms_get_asymmetric_public] +def getAsymmetricPublicKey(client, key_path): + """Retrieves the public key from a saved asymmetric key pair on Cloud KMS + """ + request = client.projects() \ + .locations() \ + .keyRings() \ + .cryptoKeys() \ + .cryptoKeyVersions() \ + .getPublicKey(name=key_path) + response = request.execute() + key_txt = response['pem'].encode('ascii') + key = serialization.load_pem_public_key(key_txt, default_backend()) + return key +# [END kms_get_asymmetric_public] + + +# [START kms_decrypt_rsa] +def decryptRSA(ciphertext, client, key_path): + """Decrypt a given ciphertext using an RSA private key stored on Cloud KMS + """ + request = client.projects() \ + .locations() \ + .keyRings() \ + .cryptoKeys() \ + .cryptoKeyVersions() \ + .asymmetricDecrypt(name=key_path, + body={'ciphertext': ciphertext}) + response = request.execute() + plaintext = base64.b64decode(response['plaintext']).decode('utf-8') + return plaintext +# [END kms_decrypt_rsa] + + +# [START kms_encrypt_rsa] +def encryptRSA(message, client, key_path): + """Encrypt message locally using an RSA public key retrieved from Cloud KMS + """ + public_key = getAsymmetricPublicKey(client, key_path) + pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None) + ciphertext = public_key.encrypt(message.encode('ascii'), pad) + ciphertext = base64.b64encode(ciphertext).decode('utf-8') + return ciphertext +# [END kms_encrypt_rsa] + + +# [START kms_sign_asymmetric] +def signAsymmetric(message, client, key_path): + """Create a signature for a message using a private key stored on Cloud KMS + """ + digest_bytes = hashlib.sha256(message.encode('ascii')).digest() + digest64 = base64.b64encode(digest_bytes) + + digest_JSON = {'sha256': digest64.decode('utf-8')} + request = client.projects() \ + .locations() \ + .keyRings() \ + .cryptoKeys() \ + .cryptoKeyVersions() \ + .asymmetricSign(name=key_path, + body={'digest': digest_JSON}) + response = request.execute() + return response.get('signature', None) +# [END kms_sign_asymmetric] + + +# [START kms_verify_signature_rsa] +def verifySignatureRSA(signature, message, client, key_path): + """Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature + for the specified plaintext message + """ + public_key = getAsymmetricPublicKey(client, key_path) + + digest_bytes = hashlib.sha256(message.encode('ascii')).digest() + sig_bytes = base64.b64decode(signature) + + try: + # Attempt verification + public_key.verify(sig_bytes, + digest_bytes, + padding.PSS(mgf=padding.MGF1(hashes.SHA256()), + salt_length=32), + utils.Prehashed(hashes.SHA256())) + # No errors were thrown. Verification was successful + return True + except InvalidSignature: + return False +# [END kms_verify_signature_rsa] + + +# [START kms_verify_signature_ec] +def verifySignatureEC(signature, message, client, key_path): + """Verify the validity of an 'EC_SIGN_P224_SHA256' signature + for the specified plaintext message + """ + public_key = getAsymmetricPublicKey(client, key_path) + + digest_bytes = hashlib.sha256(message.encode('ascii')).digest() + sig_bytes = base64.b64decode(signature) + + try: + # Attempt verification + public_key.verify(sig_bytes, + digest_bytes, + ec.ECDSA(utils.Prehashed(hashes.SHA256()))) + # No errors were thrown. Verification was successful + return True + except InvalidSignature: + return False +# [END kms_verify_signature_ec] diff --git a/samples/snippets/asymmetric_test.py b/samples/snippets/asymmetric_test.py new file mode 100644 index 00000000..20119ce5 --- /dev/null +++ b/samples/snippets/asymmetric_test.py @@ -0,0 +1,141 @@ +#!/bin/python +# Copyright 2018 Google Inc. +# +# 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. + + +from os import environ +from time import sleep + +from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePublicKey +from cryptography.hazmat.backends.openssl.rsa import _RSAPublicKey +from googleapiclient import discovery +from googleapiclient.errors import HttpError +import sample + + +def create_key_helper(key_id, key_path, purpose, algorithm, t): + try: + t.client.projects() \ + .locations() \ + .keyRings() \ + .cryptoKeys() \ + .create(parent='{}/keyRings/{}'.format(t.parent, t.keyring), + body={'purpose': purpose, + 'versionTemplate': { + 'algorithm': algorithm + } + }, + cryptoKeyId=key_id) \ + .execute() + return True + except HttpError: + # key already exists + return False + + +def setup_module(module): + """ + Set up keys in project if needed + """ + t = TestKMSSamples() + try: + # create keyring + t.client.projects() \ + .locations() \ + .keyRings() \ + .create(parent=t.parent, body={}, keyRingId=t.keyring) \ + .execute() + except HttpError: + # keyring already exists + pass + s1 = create_key_helper(t.rsaDecryptId, t.rsaDecrypt, 'ASYMMETRIC_DECRYPT', + 'RSA_DECRYPT_OAEP_2048_SHA256', t) + s2 = create_key_helper(t.rsaSignId, t.rsaSign, 'ASYMMETRIC_SIGN', + 'RSA_SIGN_PSS_2048_SHA256', t) + s3 = create_key_helper(t.ecSignId, t.ecSign, 'ASYMMETRIC_SIGN', + 'EC_SIGN_P224_SHA256', t) + if s1 or s2 or s3: + # leave time for keys to initialize + sleep(20) + + +class TestKMSSamples: + + project_id = environ['GCLOUD_PROJECT'] + keyring = 'kms-asymmetric-samples4' + parent = 'projects/{}/locations/global'.format(project_id) + + rsaSignId = 'rsa-sign' + rsaDecryptId = 'rsa-decrypt' + ecSignId = 'ec-sign' + + rsaSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ + .format(parent, keyring, rsaSignId) + rsaDecrypt = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ + .format(parent, keyring, rsaDecryptId) + ecSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ + .format(parent, keyring, ecSignId) + + message = 'test message 123' + + client = discovery.build('cloudkms', 'v1') + + def test_get_public_key(self): + rsa_key = sample.getAsymmetricPublicKey(self.client, self.rsaDecrypt) + assert isinstance(rsa_key, _RSAPublicKey), 'expected RSA key' + ec_key = sample.getAsymmetricPublicKey(self.client, self.ecSign) + assert isinstance(ec_key, _EllipticCurvePublicKey), 'expected EC key' + + def test_rsa_encrypt_decrypt(self): + ciphertext = sample.encryptRSA(self.message, + self.client, + self.rsaDecrypt) + # ciphertext should be 344 characters with base64 and RSA 2048 + assert len(ciphertext) == 344, \ + 'ciphertext should be 344 chars; got {}'.format(len(ciphertext)) + assert ciphertext[-2:] == '==', 'cipher text should end with ==' + plaintext = sample.decryptRSA(ciphertext, self.client, self.rsaDecrypt) + assert plaintext == self.message + + def test_rsa_sign_verify(self): + sig = sample.signAsymmetric(self.message, self.client, self.rsaSign) + # ciphertext should be 344 characters with base64 and RSA 2048 + assert len(sig) == 344, \ + 'sig should be 344 chars; got {}'.format(len(sig)) + assert sig[-2:] == '==', 'sig should end with ==' + success = sample.verifySignatureRSA(sig, + self.message, + self.client, + self.rsaSign) + assert success is True, 'RSA verification failed' + success = sample.verifySignatureRSA(sig, + self.message+'.', + self.client, + self.rsaSign) + assert success is False, 'verify should fail with modified message' + + def test_ec_sign_verify(self): + sig = sample.signAsymmetric(self.message, self.client, self.ecSign) + assert len(sig) > 50 and len(sig) < 300, \ + 'sig outside expected length range' + success = sample.verifySignatureEC(sig, + self.message, + self.client, + self.ecSign) + assert success is True, 'EC verification failed' + success = sample.verifySignatureEC(sig, + self.message+'.', + self.client, + self.ecSign) + assert success is False, 'verify should fail with modified message' diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index e5f3a6c5..e1899965 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,3 +1,4 @@ google-api-python-client==1.6.6 google-auth==1.4.1 google-auth-httplib2==0.0.3 +cryptography==2.3.1 From 45107a0d13606f6f7717b5abc79cc93012b9cb22 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 21 Aug 2018 18:45:02 -0700 Subject: [PATCH 26/52] kms text fixes [(#1647)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1647) --- samples/snippets/asymmetric.py | 24 +++++++++++++++++------- samples/snippets/asymmetric_test.py | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/samples/snippets/asymmetric.py b/samples/snippets/asymmetric.py index 4127148f..4d4ebcb4 100644 --- a/samples/snippets/asymmetric.py +++ b/samples/snippets/asymmetric.py @@ -24,7 +24,8 @@ # [START kms_get_asymmetric_public] def getAsymmetricPublicKey(client, key_path): - """Retrieves the public key from a saved asymmetric key pair on Cloud KMS + """ + Retrieves the public key from a saved asymmetric key pair on Cloud KMS """ request = client.projects() \ .locations() \ @@ -41,7 +42,9 @@ def getAsymmetricPublicKey(client, key_path): # [START kms_decrypt_rsa] def decryptRSA(ciphertext, client, key_path): - """Decrypt a given ciphertext using an RSA private key stored on Cloud KMS + """ + Decrypt a given ciphertext using an 'RSA_DECRYPT_OAEP_2048_SHA256' private + key stored on Cloud KMS """ request = client.projects() \ .locations() \ @@ -58,7 +61,9 @@ def decryptRSA(ciphertext, client, key_path): # [START kms_encrypt_rsa] def encryptRSA(message, client, key_path): - """Encrypt message locally using an RSA public key retrieved from Cloud KMS + """ + Encrypt message locally using an 'RSA_DECRYPT_OAEP_2048_SHA256' public + key retrieved from Cloud KMS """ public_key = getAsymmetricPublicKey(client, key_path) pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), @@ -72,8 +77,11 @@ def encryptRSA(message, client, key_path): # [START kms_sign_asymmetric] def signAsymmetric(message, client, key_path): - """Create a signature for a message using a private key stored on Cloud KMS """ + Create a signature for a message using a private key stored on Cloud KMS + """ + # Note: some key algorithms will require a different hash function + # For example, EC_SIGN_P384_SHA384 requires SHA384 digest_bytes = hashlib.sha256(message.encode('ascii')).digest() digest64 = base64.b64encode(digest_bytes) @@ -92,8 +100,9 @@ def signAsymmetric(message, client, key_path): # [START kms_verify_signature_rsa] def verifySignatureRSA(signature, message, client, key_path): - """Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature - for the specified plaintext message + """ + Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the + specified plaintext message """ public_key = getAsymmetricPublicKey(client, key_path) @@ -116,7 +125,8 @@ def verifySignatureRSA(signature, message, client, key_path): # [START kms_verify_signature_ec] def verifySignatureEC(signature, message, client, key_path): - """Verify the validity of an 'EC_SIGN_P224_SHA256' signature + """ + Verify the validity of an 'EC_SIGN_P256_SHA256' signature for the specified plaintext message """ public_key = getAsymmetricPublicKey(client, key_path) diff --git a/samples/snippets/asymmetric_test.py b/samples/snippets/asymmetric_test.py index 20119ce5..a4b1c34f 100644 --- a/samples/snippets/asymmetric_test.py +++ b/samples/snippets/asymmetric_test.py @@ -64,7 +64,7 @@ def setup_module(module): s2 = create_key_helper(t.rsaSignId, t.rsaSign, 'ASYMMETRIC_SIGN', 'RSA_SIGN_PSS_2048_SHA256', t) s3 = create_key_helper(t.ecSignId, t.ecSign, 'ASYMMETRIC_SIGN', - 'EC_SIGN_P224_SHA256', t) + 'EC_SIGN_P256_SHA256', t) if s1 or s2 or s3: # leave time for keys to initialize sleep(20) From 8cbac2eacb561d73a088558f9deaba98fc255ad1 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 7 Sep 2018 08:55:04 -0700 Subject: [PATCH 27/52] KMS test fix [(#1690)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1690) --- samples/snippets/asymmetric_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/snippets/asymmetric_test.py b/samples/snippets/asymmetric_test.py index a4b1c34f..5f969be7 100644 --- a/samples/snippets/asymmetric_test.py +++ b/samples/snippets/asymmetric_test.py @@ -21,7 +21,8 @@ from cryptography.hazmat.backends.openssl.rsa import _RSAPublicKey from googleapiclient import discovery from googleapiclient.errors import HttpError -import sample + +import asymmetric as sample def create_key_helper(key_id, key_path, purpose, algorithm, t): From c6ac72685812015f7cfb667d152fd4f569188b38 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 28 Sep 2018 16:34:48 -0700 Subject: [PATCH 28/52] KMS changes [(#1723)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1723) use byte parameters instead of strings --- samples/snippets/asymmetric.py | 41 +++++++++++++---------------- samples/snippets/asymmetric_test.py | 40 +++++++++++++++++----------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/samples/snippets/asymmetric.py b/samples/snippets/asymmetric.py index 4d4ebcb4..bc313aaa 100644 --- a/samples/snippets/asymmetric.py +++ b/samples/snippets/asymmetric.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License.rom googleapiclient import discovery +# [START kms_asymmetric_imports] import base64 import hashlib @@ -20,6 +21,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec, padding, utils +# [END kms_asymmetric_imports] # [START kms_get_asymmetric_public] @@ -43,35 +45,34 @@ def getAsymmetricPublicKey(client, key_path): # [START kms_decrypt_rsa] def decryptRSA(ciphertext, client, key_path): """ - Decrypt a given ciphertext using an 'RSA_DECRYPT_OAEP_2048_SHA256' private - key stored on Cloud KMS + Decrypt the input ciphertext (bytes) using an + 'RSA_DECRYPT_OAEP_2048_SHA256' private key stored on Cloud KMS """ + request_body = {'ciphertext': base64.b64encode(ciphertext).decode('utf-8')} request = client.projects() \ .locations() \ .keyRings() \ .cryptoKeys() \ .cryptoKeyVersions() \ .asymmetricDecrypt(name=key_path, - body={'ciphertext': ciphertext}) + body=request_body) response = request.execute() - plaintext = base64.b64decode(response['plaintext']).decode('utf-8') + plaintext = base64.b64decode(response['plaintext']) return plaintext # [END kms_decrypt_rsa] # [START kms_encrypt_rsa] -def encryptRSA(message, client, key_path): +def encryptRSA(plaintext, client, key_path): """ - Encrypt message locally using an 'RSA_DECRYPT_OAEP_2048_SHA256' public - key retrieved from Cloud KMS + Encrypt the input plaintext (bytes) locally using an + 'RSA_DECRYPT_OAEP_2048_SHA256' public key retrieved from Cloud KMS """ public_key = getAsymmetricPublicKey(client, key_path) pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None) - ciphertext = public_key.encrypt(message.encode('ascii'), pad) - ciphertext = base64.b64encode(ciphertext).decode('utf-8') - return ciphertext + return public_key.encrypt(plaintext, pad) # [END kms_encrypt_rsa] @@ -82,7 +83,7 @@ def signAsymmetric(message, client, key_path): """ # Note: some key algorithms will require a different hash function # For example, EC_SIGN_P384_SHA384 requires SHA384 - digest_bytes = hashlib.sha256(message.encode('ascii')).digest() + digest_bytes = hashlib.sha256(message).digest() digest64 = base64.b64encode(digest_bytes) digest_JSON = {'sha256': digest64.decode('utf-8')} @@ -94,7 +95,7 @@ def signAsymmetric(message, client, key_path): .asymmetricSign(name=key_path, body={'digest': digest_JSON}) response = request.execute() - return response.get('signature', None) + return base64.b64decode(response.get('signature', None)) # [END kms_sign_asymmetric] @@ -102,16 +103,14 @@ def signAsymmetric(message, client, key_path): def verifySignatureRSA(signature, message, client, key_path): """ Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the - specified plaintext message + specified message """ public_key = getAsymmetricPublicKey(client, key_path) - - digest_bytes = hashlib.sha256(message.encode('ascii')).digest() - sig_bytes = base64.b64decode(signature) + digest_bytes = hashlib.sha256(message).digest() try: # Attempt verification - public_key.verify(sig_bytes, + public_key.verify(signature, digest_bytes, padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=32), @@ -127,16 +126,14 @@ def verifySignatureRSA(signature, message, client, key_path): def verifySignatureEC(signature, message, client, key_path): """ Verify the validity of an 'EC_SIGN_P256_SHA256' signature - for the specified plaintext message + for the specified message """ public_key = getAsymmetricPublicKey(client, key_path) - - digest_bytes = hashlib.sha256(message.encode('ascii')).digest() - sig_bytes = base64.b64decode(signature) + digest_bytes = hashlib.sha256(message).digest() try: # Attempt verification - public_key.verify(sig_bytes, + public_key.verify(signature, digest_bytes, ec.ECDSA(utils.Prehashed(hashes.SHA256()))) # No errors were thrown. Verification was successful diff --git a/samples/snippets/asymmetric_test.py b/samples/snippets/asymmetric_test.py index 5f969be7..4ce9b32a 100644 --- a/samples/snippets/asymmetric_test.py +++ b/samples/snippets/asymmetric_test.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - from os import environ from time import sleep @@ -89,6 +88,7 @@ class TestKMSSamples: .format(parent, keyring, ecSignId) message = 'test message 123' + message_bytes = message.encode('utf-8') client = discovery.build('cloudkms', 'v1') @@ -99,44 +99,52 @@ def test_get_public_key(self): assert isinstance(ec_key, _EllipticCurvePublicKey), 'expected EC key' def test_rsa_encrypt_decrypt(self): - ciphertext = sample.encryptRSA(self.message, + ciphertext = sample.encryptRSA(self.message_bytes, self.client, self.rsaDecrypt) - # ciphertext should be 344 characters with base64 and RSA 2048 - assert len(ciphertext) == 344, \ - 'ciphertext should be 344 chars; got {}'.format(len(ciphertext)) - assert ciphertext[-2:] == '==', 'cipher text should end with ==' - plaintext = sample.decryptRSA(ciphertext, self.client, self.rsaDecrypt) + # ciphertext should be 256 characters with base64 and RSA 2048 + assert len(ciphertext) == 256, \ + 'ciphertext should be 256 chars; got {}'.format(len(ciphertext)) + plaintext_bytes = sample.decryptRSA(ciphertext, + self.client, + self.rsaDecrypt) + assert plaintext_bytes == self.message_bytes + plaintext = plaintext_bytes.decode('utf-8') assert plaintext == self.message def test_rsa_sign_verify(self): - sig = sample.signAsymmetric(self.message, self.client, self.rsaSign) + sig = sample.signAsymmetric(self.message_bytes, + self.client, + self.rsaSign) # ciphertext should be 344 characters with base64 and RSA 2048 - assert len(sig) == 344, \ - 'sig should be 344 chars; got {}'.format(len(sig)) - assert sig[-2:] == '==', 'sig should end with ==' + assert len(sig) == 256, \ + 'sig should be 256 chars; got {}'.format(len(sig)) success = sample.verifySignatureRSA(sig, - self.message, + self.message_bytes, self.client, self.rsaSign) assert success is True, 'RSA verification failed' + changed_bytes = self.message_bytes + b'.' success = sample.verifySignatureRSA(sig, - self.message+'.', + changed_bytes, self.client, self.rsaSign) assert success is False, 'verify should fail with modified message' def test_ec_sign_verify(self): - sig = sample.signAsymmetric(self.message, self.client, self.ecSign) + sig = sample.signAsymmetric(self.message_bytes, + self.client, + self.ecSign) assert len(sig) > 50 and len(sig) < 300, \ 'sig outside expected length range' success = sample.verifySignatureEC(sig, - self.message, + self.message_bytes, self.client, self.ecSign) assert success is True, 'EC verification failed' + changed_bytes = self.message_bytes + b'.' success = sample.verifySignatureEC(sig, - self.message+'.', + changed_bytes, self.client, self.ecSign) assert success is False, 'verify should fail with modified message' From 2b8e5e1c16776c20cd46bb9358b9f49144e3fbd6 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Thu, 25 Oct 2018 10:11:43 -0700 Subject: [PATCH 29/52] KMS import comments [(#1771)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1771) added import comments to snippets --- samples/snippets/asymmetric.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/samples/snippets/asymmetric.py b/samples/snippets/asymmetric.py index bc313aaa..7f0d11aa 100644 --- a/samples/snippets/asymmetric.py +++ b/samples/snippets/asymmetric.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License.rom googleapiclient import discovery -# [START kms_asymmetric_imports] import base64 import hashlib @@ -21,13 +20,16 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec, padding, utils -# [END kms_asymmetric_imports] # [START kms_get_asymmetric_public] def getAsymmetricPublicKey(client, key_path): """ Retrieves the public key from a saved asymmetric key pair on Cloud KMS + + Requires: + cryptography.hazmat.backends.default_backend + cryptography.hazmat.primitives.serialization """ request = client.projects() \ .locations() \ @@ -47,6 +49,9 @@ def decryptRSA(ciphertext, client, key_path): """ Decrypt the input ciphertext (bytes) using an 'RSA_DECRYPT_OAEP_2048_SHA256' private key stored on Cloud KMS + + Requires: + base64 """ request_body = {'ciphertext': base64.b64encode(ciphertext).decode('utf-8')} request = client.projects() \ @@ -67,6 +72,10 @@ def encryptRSA(plaintext, client, key_path): """ Encrypt the input plaintext (bytes) locally using an 'RSA_DECRYPT_OAEP_2048_SHA256' public key retrieved from Cloud KMS + + Requires: + cryptography.hazmat.primitives.asymmetric.padding + cryptography.hazmat.primitives.hashes """ public_key = getAsymmetricPublicKey(client, key_path) pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), @@ -80,6 +89,10 @@ def encryptRSA(plaintext, client, key_path): def signAsymmetric(message, client, key_path): """ Create a signature for a message using a private key stored on Cloud KMS + + Requires: + base64 + hashlib """ # Note: some key algorithms will require a different hash function # For example, EC_SIGN_P384_SHA384 requires SHA384 @@ -104,6 +117,13 @@ def verifySignatureRSA(signature, message, client, key_path): """ Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the specified message + + Requires: + cryptography.exceptions.InvalidSignature + cryptography.hazmat.primitives.asymmetric.padding + cryptography.hazmat.primitives.asymmetric.utils + cryptography.hazmat.primitives.hashes + hashlib """ public_key = getAsymmetricPublicKey(client, key_path) digest_bytes = hashlib.sha256(message).digest() @@ -127,6 +147,13 @@ def verifySignatureEC(signature, message, client, key_path): """ Verify the validity of an 'EC_SIGN_P256_SHA256' signature for the specified message + + Requires: + cryptography.exceptions.InvalidSignature + cryptography.hazmat.primitives.asymmetric.ec + cryptography.hazmat.primitives.asymmetric.utils + cryptography.hazmat.primitives.hashes + hashlib """ public_key = getAsymmetricPublicKey(client, key_path) digest_bytes = hashlib.sha256(message).digest() From 9a7aa6c1f9bd21c046b02543c85413bb15f2d577 Mon Sep 17 00:00:00 2001 From: DPE bot Date: Tue, 20 Nov 2018 15:40:29 -0800 Subject: [PATCH 30/52] Auto-update dependencies. [(#1846)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1846) ACK, merging. --- samples/snippets/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index e1899965..36c6e965 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-api-python-client==1.6.6 -google-auth==1.4.1 +google-api-python-client==1.7.4 +google-auth==1.6.1 google-auth-httplib2==0.0.3 -cryptography==2.3.1 +cryptography==2.4.1 From 0f278184f21770c51fc7b457f46b9d57c9ef3c1b Mon Sep 17 00:00:00 2001 From: DPE bot Date: Wed, 21 Nov 2018 10:25:04 -0800 Subject: [PATCH 31/52] Auto-update dependencies. [(#1862)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1862) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 36c6e965..e75d6b4d 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ google-api-python-client==1.7.4 google-auth==1.6.1 google-auth-httplib2==0.0.3 -cryptography==2.4.1 +cryptography==2.4.2 From 15d486d6b644b8daad0042f8e6069abeb64a9c2f Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Mon, 7 Jan 2019 13:08:18 -0800 Subject: [PATCH 32/52] KMS: Updated for new client library [(#1903)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1903) * updated kms samples to use new gapic client library --- samples/snippets/README.rst | 15 +- samples/snippets/README.rst.in | 4 +- samples/snippets/asymmetric.py | 137 +++++--- samples/snippets/asymmetric_test.py | 133 ++++---- samples/snippets/quickstart.py | 21 +- samples/snippets/requirements.txt | 4 +- samples/snippets/snippets.py | 510 +++++++++++----------------- samples/snippets/snippets_test.py | 361 +++++++++++--------- 8 files changed, 573 insertions(+), 612 deletions(-) diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 28777c84..17e34bef 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -92,18 +92,5 @@ To run this sample: $ python snippets.py - usage: snippets.py [-h] - {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,enable_crypto_key_version,destroy_crypto_key_version,restore_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} - ... - positional arguments: - {create_key_ring,create_crypto_key,encrypt,decrypt,disable_crypto_key_version,enable_crypto_key_version,destroy_crypto_key_version,restore_crypto_key_version,add_member_to_crypto_key_policy,get_key_ring_policy} - - optional arguments: - -h, --help show this help message and exit - - - - - -.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file +.. _Google Cloud SDK: https://cloud.google.com/sdk/ diff --git a/samples/snippets/README.rst.in b/samples/snippets/README.rst.in index 6299b5a0..f8aef3a2 100644 --- a/samples/snippets/README.rst.in +++ b/samples/snippets/README.rst.in @@ -18,5 +18,7 @@ samples: - name: Snippets file: snippets.py show_help: True +- name: Asymmetric + file: asymmetric.py -folder: kms/api-client \ No newline at end of file +folder: kms/api-client diff --git a/samples/snippets/asymmetric.py b/samples/snippets/asymmetric.py index 7f0d11aa..9c8abd15 100644 --- a/samples/snippets/asymmetric.py +++ b/samples/snippets/asymmetric.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License.rom googleapiclient import discovery -import base64 import hashlib from cryptography.exceptions import InvalidSignature @@ -21,63 +20,96 @@ from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec, padding, utils +from google.cloud import kms_v1 +from google.cloud.kms_v1 import enums + + +# [START kms_create_asymmetric_key] +def create_asymmetric_key(project_id, location_id, key_ring_id, crypto_key_id): + """Creates an RSA encrypt/decrypt key pair within a specified KeyRing.""" + + # Creates an API client for the KMS API. + client = kms_v1.KeyManagementServiceClient() + + # The resource name of the KeyRing associated with the CryptoKey. + parent = client.key_ring_path(project_id, location_id, key_ring_id) + + # Create the CryptoKey object template + purpose = enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT + algorithm = enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.\ + RSA_DECRYPT_OAEP_2048_SHA256 + crypto_key = {'purpose': purpose, + 'version_template': {'algorithm': algorithm}} + + # Create a CryptoKey for the given KeyRing. + response = client.create_crypto_key(parent, crypto_key_id, crypto_key) + + print('Created CryptoKey {}.'.format(response.name)) + return response +# [END kms_create_asymmetric_key] + # [START kms_get_asymmetric_public] -def getAsymmetricPublicKey(client, key_path): +def get_asymmetric_public_key(key_name): """ Retrieves the public key from a saved asymmetric key pair on Cloud KMS + Example key_name: + "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ + /KEY_ID/cryptoKeyVersions/1" + Requires: cryptography.hazmat.backends.default_backend cryptography.hazmat.primitives.serialization """ - request = client.projects() \ - .locations() \ - .keyRings() \ - .cryptoKeys() \ - .cryptoKeyVersions() \ - .getPublicKey(name=key_path) - response = request.execute() - key_txt = response['pem'].encode('ascii') + + client = kms_v1.KeyManagementServiceClient() + response = client.get_public_key(key_name) + + key_txt = response.pem.encode('ascii') key = serialization.load_pem_public_key(key_txt, default_backend()) return key # [END kms_get_asymmetric_public] # [START kms_decrypt_rsa] -def decryptRSA(ciphertext, client, key_path): +def decrypt_rsa(ciphertext, key_name): """ Decrypt the input ciphertext (bytes) using an 'RSA_DECRYPT_OAEP_2048_SHA256' private key stored on Cloud KMS - Requires: - base64 + Example key_name: + "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ + /KEY_ID/cryptoKeyVersions/1" """ - request_body = {'ciphertext': base64.b64encode(ciphertext).decode('utf-8')} - request = client.projects() \ - .locations() \ - .keyRings() \ - .cryptoKeys() \ - .cryptoKeyVersions() \ - .asymmetricDecrypt(name=key_path, - body=request_body) - response = request.execute() - plaintext = base64.b64decode(response['plaintext']) - return plaintext + + client = kms_v1.KeyManagementServiceClient() + response = client.asymmetric_decrypt(key_name, ciphertext) + return response.plaintext # [END kms_decrypt_rsa] # [START kms_encrypt_rsa] -def encryptRSA(plaintext, client, key_path): +def encrypt_rsa(plaintext, key_name): """ Encrypt the input plaintext (bytes) locally using an 'RSA_DECRYPT_OAEP_2048_SHA256' public key retrieved from Cloud KMS + Example key_name: + "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ + /KEY_ID/cryptoKeyVersions/1" + Requires: cryptography.hazmat.primitives.asymmetric.padding cryptography.hazmat.primitives.hashes """ - public_key = getAsymmetricPublicKey(client, key_path) + # get the public key + client = kms_v1.KeyManagementServiceClient() + response = client.get_public_key(key_name) + key_txt = response.pem.encode('ascii') + public_key = serialization.load_pem_public_key(key_txt, default_backend()) + + # encrypt plaintext pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None) @@ -86,38 +118,39 @@ def encryptRSA(plaintext, client, key_path): # [START kms_sign_asymmetric] -def signAsymmetric(message, client, key_path): +def sign_asymmetric(message, key_name): """ Create a signature for a message using a private key stored on Cloud KMS + Example key_name: + "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ + /KEY_ID/cryptoKeyVersions/1" + Requires: - base64 hashlib """ # Note: some key algorithms will require a different hash function # For example, EC_SIGN_P384_SHA384 requires SHA384 + client = kms_v1.KeyManagementServiceClient() digest_bytes = hashlib.sha256(message).digest() - digest64 = base64.b64encode(digest_bytes) - - digest_JSON = {'sha256': digest64.decode('utf-8')} - request = client.projects() \ - .locations() \ - .keyRings() \ - .cryptoKeys() \ - .cryptoKeyVersions() \ - .asymmetricSign(name=key_path, - body={'digest': digest_JSON}) - response = request.execute() - return base64.b64decode(response.get('signature', None)) + + digest_json = {'sha256': digest_bytes} + + response = client.asymmetric_sign(key_name, digest_json) + return response.signature # [END kms_sign_asymmetric] # [START kms_verify_signature_rsa] -def verifySignatureRSA(signature, message, client, key_path): +def verify_signature_rsa(signature, message, key_name): """ Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the specified message + Example key_name: + "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ + /KEY_ID/cryptoKeyVersions/1" + Requires: cryptography.exceptions.InvalidSignature cryptography.hazmat.primitives.asymmetric.padding @@ -125,7 +158,13 @@ def verifySignatureRSA(signature, message, client, key_path): cryptography.hazmat.primitives.hashes hashlib """ - public_key = getAsymmetricPublicKey(client, key_path) + # get the public key + client = kms_v1.KeyManagementServiceClient() + response = client.get_public_key(key_name) + key_txt = response.pem.encode('ascii') + public_key = serialization.load_pem_public_key(key_txt, default_backend()) + + # get the digest of the message digest_bytes = hashlib.sha256(message).digest() try: @@ -143,11 +182,15 @@ def verifySignatureRSA(signature, message, client, key_path): # [START kms_verify_signature_ec] -def verifySignatureEC(signature, message, client, key_path): +def verify_signature_ec(signature, message, key_name): """ Verify the validity of an 'EC_SIGN_P256_SHA256' signature for the specified message + Example key_name: + "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ + /KEY_ID/cryptoKeyVersions/1" + Requires: cryptography.exceptions.InvalidSignature cryptography.hazmat.primitives.asymmetric.ec @@ -155,7 +198,13 @@ def verifySignatureEC(signature, message, client, key_path): cryptography.hazmat.primitives.hashes hashlib """ - public_key = getAsymmetricPublicKey(client, key_path) + # get the public key + client = kms_v1.KeyManagementServiceClient() + response = client.get_public_key(key_name) + key_txt = response.pem.encode('ascii') + public_key = serialization.load_pem_public_key(key_txt, default_backend()) + + # get the digest of the message digest_bytes = hashlib.sha256(message).digest() try: diff --git a/samples/snippets/asymmetric_test.py b/samples/snippets/asymmetric_test.py index 4ce9b32a..cc621003 100644 --- a/samples/snippets/asymmetric_test.py +++ b/samples/snippets/asymmetric_test.py @@ -16,135 +16,118 @@ from os import environ from time import sleep +import asymmetric + from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePublicKey from cryptography.hazmat.backends.openssl.rsa import _RSAPublicKey -from googleapiclient import discovery -from googleapiclient.errors import HttpError -import asymmetric as sample +from google.api_core.exceptions import GoogleAPICallError +from google.cloud.kms_v1 import enums +from snippets import create_key_ring -def create_key_helper(key_id, key_path, purpose, algorithm, t): - try: - t.client.projects() \ - .locations() \ - .keyRings() \ - .cryptoKeys() \ - .create(parent='{}/keyRings/{}'.format(t.parent, t.keyring), - body={'purpose': purpose, - 'versionTemplate': { - 'algorithm': algorithm - } - }, - cryptoKeyId=key_id) \ - .execute() - return True - except HttpError: - # key already exists - return False +from snippets_test import create_key_helper def setup_module(module): """ Set up keys in project if needed """ - t = TestKMSSamples() + t = TestKMSAsymmetric() try: # create keyring - t.client.projects() \ - .locations() \ - .keyRings() \ - .create(parent=t.parent, body={}, keyRingId=t.keyring) \ - .execute() - except HttpError: + create_key_ring(t.project_id, t.location, t.keyring_id) + except GoogleAPICallError: # keyring already exists pass - s1 = create_key_helper(t.rsaDecryptId, t.rsaDecrypt, 'ASYMMETRIC_DECRYPT', - 'RSA_DECRYPT_OAEP_2048_SHA256', t) - s2 = create_key_helper(t.rsaSignId, t.rsaSign, 'ASYMMETRIC_SIGN', - 'RSA_SIGN_PSS_2048_SHA256', t) - s3 = create_key_helper(t.ecSignId, t.ecSign, 'ASYMMETRIC_SIGN', - 'EC_SIGN_P256_SHA256', t) + s1 = create_key_helper(t.rsaDecryptId, + enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT, + enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. + RSA_DECRYPT_OAEP_2048_SHA256, + t) + s2 = create_key_helper(t.rsaSignId, + enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. + RSA_SIGN_PSS_2048_SHA256, + t) + s3 = create_key_helper(t.ecSignId, + enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. + EC_SIGN_P256_SHA256, + t) + if s1 or s2 or s3: # leave time for keys to initialize sleep(20) -class TestKMSSamples: - +class TestKMSAsymmetric: project_id = environ['GCLOUD_PROJECT'] - keyring = 'kms-asymmetric-samples4' - parent = 'projects/{}/locations/global'.format(project_id) + keyring_id = 'kms-samples' + location = 'global' + parent = 'projects/{}/locations/{}'.format(project_id, location) rsaSignId = 'rsa-sign' rsaDecryptId = 'rsa-decrypt' ecSignId = 'ec-sign' rsaSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring, rsaSignId) + .format(parent, keyring_id, rsaSignId) rsaDecrypt = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring, rsaDecryptId) + .format(parent, keyring_id, rsaDecryptId) ecSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring, ecSignId) + .format(parent, keyring_id, ecSignId) message = 'test message 123' message_bytes = message.encode('utf-8') - client = discovery.build('cloudkms', 'v1') - def test_get_public_key(self): - rsa_key = sample.getAsymmetricPublicKey(self.client, self.rsaDecrypt) + rsa_key = asymmetric.get_asymmetric_public_key(self.rsaDecrypt) + assert isinstance(rsa_key, _RSAPublicKey), 'expected RSA key' + rsa_key = asymmetric.get_asymmetric_public_key(self.rsaSign) assert isinstance(rsa_key, _RSAPublicKey), 'expected RSA key' - ec_key = sample.getAsymmetricPublicKey(self.client, self.ecSign) + ec_key = asymmetric.get_asymmetric_public_key(self.ecSign) assert isinstance(ec_key, _EllipticCurvePublicKey), 'expected EC key' def test_rsa_encrypt_decrypt(self): - ciphertext = sample.encryptRSA(self.message_bytes, - self.client, - self.rsaDecrypt) - # ciphertext should be 256 characters with base64 and RSA 2048 + ciphertext = asymmetric.encrypt_rsa(self.message_bytes, + self.rsaDecrypt) + # signature should be 256 bytes for RSA 2048 assert len(ciphertext) == 256, \ 'ciphertext should be 256 chars; got {}'.format(len(ciphertext)) - plaintext_bytes = sample.decryptRSA(ciphertext, - self.client, - self.rsaDecrypt) + plaintext_bytes = asymmetric.decrypt_rsa(ciphertext, + self.rsaDecrypt) assert plaintext_bytes == self.message_bytes plaintext = plaintext_bytes.decode('utf-8') assert plaintext == self.message def test_rsa_sign_verify(self): - sig = sample.signAsymmetric(self.message_bytes, - self.client, - self.rsaSign) - # ciphertext should be 344 characters with base64 and RSA 2048 + sig = asymmetric.sign_asymmetric(self.message_bytes, + self.rsaSign) + # signature should be 256 bytes for RSA 2048 assert len(sig) == 256, \ 'sig should be 256 chars; got {}'.format(len(sig)) - success = sample.verifySignatureRSA(sig, - self.message_bytes, - self.client, - self.rsaSign) + success = asymmetric.verify_signature_rsa(sig, + self.message_bytes, + self.rsaSign) assert success is True, 'RSA verification failed' changed_bytes = self.message_bytes + b'.' - success = sample.verifySignatureRSA(sig, - changed_bytes, - self.client, - self.rsaSign) + success = asymmetric.verify_signature_rsa(sig, + changed_bytes, + self.rsaSign) assert success is False, 'verify should fail with modified message' def test_ec_sign_verify(self): - sig = sample.signAsymmetric(self.message_bytes, - self.client, - self.ecSign) + sig = asymmetric.sign_asymmetric(self.message_bytes, + self.ecSign) assert len(sig) > 50 and len(sig) < 300, \ 'sig outside expected length range' - success = sample.verifySignatureEC(sig, - self.message_bytes, - self.client, - self.ecSign) + success = asymmetric.verify_signature_ec(sig, + self.message_bytes, + self.ecSign) assert success is True, 'EC verification failed' changed_bytes = self.message_bytes + b'.' - success = sample.verifySignatureEC(sig, - changed_bytes, - self.client, - self.ecSign) + success = asymmetric.verify_signature_ec(sig, + changed_bytes, + self.ecSign) assert success is False, 'verify should fail with modified message' diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 042bcae9..2f97f38f 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -13,32 +13,37 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and +import os + def run_quickstart(): # [START kms_quickstart] # Imports the Google APIs client library - import googleapiclient.discovery + from google.cloud import kms_v1 # Your Google Cloud Platform project ID project_id = 'YOUR_PROJECT_ID' + # [END kms_quickstart] + project_id = os.environ['GCLOUD_PROJECT'] + # [START kms_quickstart] # Lists keys in the "global" location. location = 'global' # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # The resource name of the location associated with the key rings. - parent = 'projects/{}/locations/{}'.format(project_id, location) + parent = client.location_path(project_id, location) # Lists key rings - request = kms_client.projects().locations().keyRings().list(parent=parent) - response = request.execute() + response = client.list_key_rings(parent) + response_list = list(response) - if 'keyRings' in response and response['keyRings']: + if len(response_list) > 0: print('Key rings:') - for key_ring in response['keyRings']: - print(key_ring['name']) + for key_ring in response_list: + print(key_ring.name) else: print('No key rings found.') # [END kms_quickstart] diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index e75d6b4d..74f7afd7 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,2 @@ -google-api-python-client==1.7.4 -google-auth==1.6.1 -google-auth-httplib2==0.0.3 +google-cloud-kms==0.2 cryptography==2.4.2 diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index 73e4a65d..07b02705 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -13,11 +13,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -import argparse -import base64 -import io -import googleapiclient.discovery +from google.cloud import kms_v1 +from google.cloud.kms_v1 import enums # [START kms_create_keyring] @@ -25,17 +23,20 @@ def create_key_ring(project_id, location_id, key_ring_id): """Creates a KeyRing in the given location (e.g. global).""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # The resource name of the location associated with the KeyRing. - parent = 'projects/{}/locations/{}'.format(project_id, location_id) + parent = client.location_path(project_id, location_id) - # Create KeyRing - request = kms_client.projects().locations().keyRings().create( - parent=parent, body={}, keyRingId=key_ring_id) - response = request.execute() + # The keyring object template + keyring_name = client.key_ring_path(project_id, location_id, key_ring_id) + keyring = {'name': keyring_name} - print('Created KeyRing {}.'.format(response['name'])) + # Create a KeyRing + response = client.create_key_ring(parent, key_ring_id, keyring) + + print('Created KeyRing {}.'.format(response.name)) + return response # [END kms_create_keyring] @@ -44,87 +45,55 @@ def create_crypto_key(project_id, location_id, key_ring_id, crypto_key_id): """Creates a CryptoKey within a KeyRing in the given location.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # The resource name of the KeyRing associated with the CryptoKey. - parent = 'projects/{}/locations/{}/keyRings/{}'.format( - project_id, location_id, key_ring_id) + parent = client.key_ring_path(project_id, location_id, key_ring_id) + + # Create the CryptoKey object template + purpose = enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + crypto_key = {'purpose': purpose} # Create a CryptoKey for the given KeyRing. - request = kms_client.projects().locations().keyRings().cryptoKeys().create( - parent=parent, body={'purpose': 'ENCRYPT_DECRYPT'}, - cryptoKeyId=crypto_key_id) - response = request.execute() + response = client.create_crypto_key(parent, crypto_key_id, crypto_key) - print('Created CryptoKey {}.'.format(response['name'])) + print('Created CryptoKey {}.'.format(response.name)) + return response # [END kms_create_cryptokey] # [START kms_encrypt] -def encrypt(project_id, location_id, key_ring_id, crypto_key_id, - plaintext_file_name, ciphertext_file_name): - """Encrypts data from plaintext_file_name using the provided CryptoKey and - saves it to ciphertext_file_name so it can only be recovered with a call to - decrypt. - """ +def encrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, + plaintext): + """Encrypts input plaintext data using the provided symmetric CryptoKey.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - project_id, location_id, key_ring_id, crypto_key_id) - - # Read data from the input file. - with io.open(plaintext_file_name, 'rb') as plaintext_file: - plaintext = plaintext_file.read() + name = client.crypto_key_path_path(project_id, location_id, key_ring_id, + crypto_key_id) # Use the KMS API to encrypt the data. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - request = crypto_keys.encrypt( - name=name, - body={'plaintext': base64.b64encode(plaintext).decode('ascii')}) - response = request.execute() - ciphertext = base64.b64decode(response['ciphertext'].encode('ascii')) - - # Write the encrypted data to a file. - with io.open(ciphertext_file_name, 'wb') as ciphertext_file: - ciphertext_file.write(ciphertext) - - print('Saved ciphertext to {}.'.format(ciphertext_file_name)) + response = client.encrypt(name, plaintext) + return response.ciphertext # [END kms_encrypt] # [START kms_decrypt] -def decrypt(project_id, location_id, key_ring_id, crypto_key_id, - ciphertext_file_name, plaintext_file_name): - """Decrypts data from ciphertext_file_name that was previously encrypted - using the provided CryptoKey and saves it to plaintext_file_name.""" +def decrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, + ciphertext): + """Decrypts input ciphertext using the provided symmetric CryptoKey.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - name = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - project_id, location_id, key_ring_id, crypto_key_id) - - # Read encrypted data from the input file. - with io.open(ciphertext_file_name, 'rb') as ciphertext_file: - ciphertext = ciphertext_file.read() - + name = client.crypto_key_path_path(project_id, location_id, key_ring_id, + crypto_key_id) # Use the KMS API to decrypt the data. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - request = crypto_keys.decrypt( - name=name, - body={'ciphertext': base64.b64encode(ciphertext).decode('ascii')}) - response = request.execute() - plaintext = base64.b64decode(response['plaintext'].encode('ascii')) - - # Write the decrypted data to a file. - with io.open(plaintext_file_name, 'wb') as plaintext_file: - plaintext_file.write(plaintext) - - print('Saved plaintext to {}.'.format(plaintext_file_name)) + response = client.decrypt(name, ciphertext) + return response.plaintext # [END kms_decrypt] @@ -135,23 +104,21 @@ def disable_crypto_key_version(project_id, location_id, key_ring_id, KeyRing.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # Construct the resource name of the CryptoKeyVersion. - name = ( - 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}' - .format( - project_id, location_id, key_ring_id, crypto_key_id, version_id)) + name = client.crypto_key_version_path(project_id, location_id, key_ring_id, + crypto_key_id, version_id) # Use the KMS API to disable the CryptoKeyVersion. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - request = crypto_keys.cryptoKeyVersions().patch( - name=name, body={'state': 'DISABLED'}, updateMask='state') - response = request.execute() + new_state = enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + version = {'name': name, 'state': new_state} + update_mask = {'paths': ["state"]} + # Print results + response = client.update_crypto_key_version(version, update_mask) print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response['state'])) + name, response.state)) # [END kms_disable_cryptokey_version] @@ -162,23 +129,21 @@ def enable_crypto_key_version(project_id, location_id, key_ring_id, KeyRing.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # Construct the resource name of the CryptoKeyVersion. - name = ( - 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}' - .format( - project_id, location_id, key_ring_id, crypto_key_id, version_id)) + name = client.crypto_key_version_path(project_id, location_id, key_ring_id, + crypto_key_id, version_id) # Use the KMS API to enable the CryptoKeyVersion. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - request = crypto_keys.cryptoKeyVersions().patch( - name=name, body={'state': 'ENABLED'}, updateMask='state') - response = request.execute() + new_state = enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED + version = {'name': name, 'state': new_state} + update_mask = {'paths': ["state"]} + # Print results + response = client.update_crypto_key_version(version, update_mask) print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response['state'])) + name, response.state)) # [END kms_enable_cryptokey_version] @@ -189,22 +154,18 @@ def destroy_crypto_key_version( KeyRing for destruction 24 hours in the future.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # Construct the resource name of the CryptoKeyVersion. - name = ( - 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}' - .format( - project_id, location_id, key_ring_id, crypto_key_id, version_id)) + name = client.crypto_key_version_path(project_id, location_id, key_ring_id, + crypto_key_id, version_id) - # Use the KMS API to schedule the CryptoKeyVersion for destruction. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - request = crypto_keys.cryptoKeyVersions().destroy(name=name, body={}) - response = request.execute() + # Use the KMS API to mark the CryptoKeyVersion for destruction. + response = client.destroy_crypto_key_version(name) + # Print results print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response['state'])) + name, response.state)) # [END kms_destroy_cryptokey_version] @@ -214,22 +175,20 @@ def restore_crypto_key_version( """Restores a CryptoKeyVersion that is scheduled for destruction.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # Construct the resource name of the CryptoKeyVersion. - name = ( - 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}' - .format( - project_id, location_id, key_ring_id, crypto_key_id, version_id)) + name = client.crypto_key_version_path(project_id, location_id, key_ring_id, + crypto_key_id, version_id) # Use the KMS API to restore the CryptoKeyVersion. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - request = crypto_keys.cryptoKeyVersions().restore(name=name, body={}) - response = request.execute() + response = client.restore_crypto_key_version(name) + # Print results print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response['state'])) + name, response.state)) + + # [END kms_restore_cryptokey_version] @@ -240,217 +199,154 @@ def add_member_to_crypto_key_policy( (IAM) policy for a given CryptoKey associated with a KeyRing.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - project_id, location_id, key_ring_id, crypto_key_id) - - # Get the current IAM policy and add the new member to it. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - policy_request = crypto_keys.getIamPolicy(resource=parent) - policy_response = policy_request.execute() - bindings = [] - if 'bindings' in policy_response.keys(): - bindings = policy_response['bindings'] - members = [] - members.append(member) - new_binding = dict() - new_binding['role'] = role - new_binding['members'] = members - bindings.append(new_binding) - policy_response['bindings'] = bindings - - # Set the new IAM Policy. - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - request = crypto_keys.setIamPolicy( - resource=parent, body={'policy': policy_response}) - request.execute() - - print_msg = ( - 'Member {} added with role {} to policy for CryptoKey {} in KeyRing {}' - .format(member, role, crypto_key_id, key_ring_id)) - print(print_msg) + resource = client.crypto_key_path_path(project_id, location_id, + key_ring_id, crypto_key_id) + # Get the current IAM policy. + policy = client.get_iam_policy(resource) + + # Add member + policy.bindings.add( + role=role, + members=[member]) + + # Update the IAM Policy. + client.set_iam_policy(resource, policy) + + # Print results + print('Member {} added with role {} to policy for CryptoKey {} \ + in KeyRing {}'.format(member, role, crypto_key_id, key_ring_id)) # [END kms_add_member_to_cryptokey_policy] +# [START kms_add_member_to_keyring_policy] +def add_member_to_key_ring_policy( + project_id, location_id, key_ring_id, member, role): + """Adds a member with a given role to the Identity and Access Management + (IAM) policy for a given KeyRing.""" + + # Creates an API client for the KMS API. + client = kms_v1.KeyManagementServiceClient() + + # The resource name of the KeyRing. + resource = client.key_ring_path(project_id, location_id, key_ring_id) + + # Get the current IAM policy. + policy = client.get_iam_policy(resource) + + # Add member + policy.bindings.add( + role=role, + members=[member]) + + # Update the IAM Policy. + client.set_iam_policy(resource, policy) + + # Print results + print('Member {} added with role {} to policy in KeyRing {}' + .format(member, role, key_ring_id)) + +# [END kms_add_member_to_keyring_policy] + + +# [START kms_remove_member_from_cryptokey_policy] +def remove_member_from_crypto_key_policy( + project_id, location_id, key_ring_id, crypto_key_id, member, role): + """Removes a member with a given role from the Identity and Access + Management (IAM) policy for a given CryptoKey associated with a KeyRing.""" + + # Creates an API client for the KMS API. + client = kms_v1.KeyManagementServiceClient() + + # The resource name of the CryptoKey. + resource = client.crypto_key_path_path(project_id, location_id, + key_ring_id, crypto_key_id) + # Get the current IAM policy. + policy = client.get_iam_policy(resource) + + # Remove member + for b in list(policy.bindings): + if b.role == role and member in b.members: + b.members.remove(member) + + # Update the IAM Policy. + client.set_iam_policy(resource, policy) + + # Print results + print('Member {} removed from role {} for CryptoKey in KeyRing {}' + .format(member, role, crypto_key_id, key_ring_id)) +# [END kms_remove_member_from_cryptokey_policy] + + +def remove_member_from_key_ring_policy(project_id, location_id, key_ring_id, + member, role): + """Removes a member with a given role from the Identity and Access + Management (IAM) policy for a given KeyRing.""" + + # Creates an API client for the KMS API. + client = kms_v1.KeyManagementServiceClient() + + # The resource name of the KeyRing. + resource = client.key_ring_path(project_id, location_id, key_ring_id) + + # Get the current IAM policy. + policy = client.get_iam_policy(resource) + + # Remove member + for b in list(policy.bindings): + if b.role == role and member in b.members: + b.members.remove(member) + + # Update the IAM Policy. + client.set_iam_policy(resource, policy) + + # Print results + print('Member {} removed from role {} for KeyRing {}' + .format(member, role, key_ring_id)) + + # [START kms_get_keyring_policy] def get_key_ring_policy(project_id, location_id, key_ring_id): """Gets the Identity and Access Management (IAM) policy for a given KeyRing and prints out roles and the members assigned to those roles.""" # Creates an API client for the KMS API. - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') + client = kms_v1.KeyManagementServiceClient() # The resource name of the KeyRing. - parent = 'projects/{}/locations/{}/keyRings/{}'.format( - project_id, location_id, key_ring_id) + resource = client.key_ring_path(project_id, location_id, key_ring_id) # Get the current IAM policy. - request = kms_client.projects().locations().keyRings().getIamPolicy( - resource=parent) - response = request.execute() - - if 'bindings' in response.keys(): - print('Printing IAM policy for resource {}:'.format(parent)) - for binding in response['bindings']: - print('') - print('Role: {}'.format(binding['role'])) - print('Members:') - for member in binding['members']: - print(member) - print('') - else: - print('No roles found for resource {}.'.format(parent)) + policy = client.get_iam_policy(resource) + + # Print results + print('Printing IAM policy for resource {}:'.format(resource)) + for b in policy.bindings: + for m in b.members: + print('Role: {} Member: {}'.format(b.role, m)) + return policy # [END kms_get_keyring_policy] -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) - subparsers = parser.add_subparsers(dest='command') - - create_key_ring_parser = subparsers.add_parser('create_key_ring') - create_key_ring_parser.add_argument('project') - create_key_ring_parser.add_argument('location') - create_key_ring_parser.add_argument('key_ring') - - create_crypto_key_parser = subparsers.add_parser('create_crypto_key') - create_crypto_key_parser.add_argument('project') - create_crypto_key_parser.add_argument('location') - create_crypto_key_parser.add_argument('key_ring') - create_crypto_key_parser.add_argument('crypto_key') - - encrypt_parser = subparsers.add_parser('encrypt') - encrypt_parser.add_argument('project') - encrypt_parser.add_argument('location') - encrypt_parser.add_argument('key_ring') - encrypt_parser.add_argument('crypto_key') - encrypt_parser.add_argument('infile') - encrypt_parser.add_argument('outfile') - - decrypt_parser = subparsers.add_parser('decrypt') - decrypt_parser.add_argument('project') - decrypt_parser.add_argument('location') - decrypt_parser.add_argument('key_ring') - decrypt_parser.add_argument('crypto_key') - decrypt_parser.add_argument('infile') - decrypt_parser.add_argument('outfile') - - disable_crypto_key_version_parser = subparsers.add_parser( - 'disable_crypto_key_version') - disable_crypto_key_version_parser.add_argument('project') - disable_crypto_key_version_parser.add_argument('location') - disable_crypto_key_version_parser.add_argument('key_ring') - disable_crypto_key_version_parser.add_argument('crypto_key') - disable_crypto_key_version_parser.add_argument('version') - - enable_crypto_key_version_parser = subparsers.add_parser( - 'enable_crypto_key_version') - enable_crypto_key_version_parser.add_argument('project') - enable_crypto_key_version_parser.add_argument('location') - enable_crypto_key_version_parser.add_argument('key_ring') - enable_crypto_key_version_parser.add_argument('crypto_key') - enable_crypto_key_version_parser.add_argument('version') - - destroy_crypto_key_version_parser = subparsers.add_parser( - 'destroy_crypto_key_version') - destroy_crypto_key_version_parser.add_argument('project') - destroy_crypto_key_version_parser.add_argument('location') - destroy_crypto_key_version_parser.add_argument('key_ring') - destroy_crypto_key_version_parser.add_argument('crypto_key') - destroy_crypto_key_version_parser.add_argument('version') - - restore_crypto_key_version_parser = subparsers.add_parser( - 'restore_crypto_key_version') - restore_crypto_key_version_parser.add_argument('project') - restore_crypto_key_version_parser.add_argument('location') - restore_crypto_key_version_parser.add_argument('key_ring') - restore_crypto_key_version_parser.add_argument('crypto_key') - restore_crypto_key_version_parser.add_argument('version') - - add_member_to_crypto_key_policy_parser = subparsers.add_parser( - 'add_member_to_crypto_key_policy') - add_member_to_crypto_key_policy_parser.add_argument('project') - add_member_to_crypto_key_policy_parser.add_argument('location') - add_member_to_crypto_key_policy_parser.add_argument('key_ring') - add_member_to_crypto_key_policy_parser.add_argument('crypto_key') - add_member_to_crypto_key_policy_parser.add_argument('member') - add_member_to_crypto_key_policy_parser.add_argument('role') - - get_key_ring_policy_parser = subparsers.add_parser('get_key_ring_policy') - get_key_ring_policy_parser.add_argument('project') - get_key_ring_policy_parser.add_argument('location') - get_key_ring_policy_parser.add_argument('key_ring') - - args = parser.parse_args() - - if args.command == 'create_key_ring': - create_key_ring( - args.project, - args.location, - args.key_ring) - elif args.command == 'create_crypto_key': - create_crypto_key( - args.project, - args.location, - args.key_ring, - args.crypto_key) - elif args.command == 'encrypt': - encrypt( - args.project, - args.location, - args.key_ring, - args.crypto_key, - args.infile, - args.outfile) - elif args.command == 'decrypt': - decrypt( - args.project, - args.location, - args.key_ring, - args.crypto_key, - args.infile, - args.outfile) - elif args.command == 'disable_crypto_key_version': - disable_crypto_key_version( - args.project, - args.location, - args.key_ring, - args.crypto_key, - args.version) - elif args.command == 'enable_crypto_key_version': - enable_crypto_key_version( - args.project, - args.location, - args.key_ring, - args.crypto_key, - args.version) - elif args.command == 'destroy_crypto_key_version': - destroy_crypto_key_version( - args.project, - args.location, - args.key_ring, - args.crypto_key, - args.version) - elif args.command == 'restore_crypto_key_version': - restore_crypto_key_version( - args.project, - args.location, - args.key_ring, - args.crypto_key, - args.version) - elif args.command == 'add_member_to_crypto_key_policy': - add_member_to_crypto_key_policy( - args.project, - args.location, - args.key_ring, - args.crypto_key, - args.member, - args.role) - elif args.command == 'get_key_ring_policy': - get_key_ring_policy( - args.project, - args.location, - args.key_ring) +def get_crypto_key_policy(project_id, location_id, key_ring_id, crypto_key_id): + """Gets the Identity and Access Management (IAM) policy for a given KeyRing + and prints out roles and the members assigned to those roles.""" + + # Creates an API client for the KMS API. + client = kms_v1.KeyManagementServiceClient() + + # The resource name of the CryptoKey. + resource = client.crypto_key_path_path(project_id, location_id, + key_ring_id, crypto_key_id) + + # Get the current IAM policy. + policy = client.get_iam_policy(resource) + + # Print results + print('Printing IAM policy for resource {}:'.format(resource)) + for b in policy.bindings: + for m in b.members: + print('Role: {} Member: {}'.format(b.role, m)) + return policy diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index b36d9644..9da03d9c 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -13,167 +13,208 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -import os -import random -import string +import time +from os import environ -import googleapiclient.discovery +from google.api_core.exceptions import GoogleAPICallError +from google.cloud import kms_v1 +from google.cloud.kms_v1 import enums +from google.iam.v1.policy_pb2 import Policy + +import pytest import snippets -PROJECT = os.environ['GCLOUD_PROJECT'] - -# Your Google Cloud Platform Key Location -LOCATION = 'global' - -# Your Google Cloud Platform KeyRing name -KEY_RING = ''.join( - random.choice(string.ascii_lowercase + string.digits) for _ in range(12)) - -# Your Google Cloud Platform CryptoKey name -CRYPTO_KEY = ''.join( - random.choice(string.ascii_lowercase + string.digits) for _ in range(12)) - -# Your Google Cloud Platform CryptoKeyVersion name -VERSION = 1 - -# A member to add to our IAM policy -MEMBER = 'user:ryanmats@google.com' - -# The role we want our new member to have for our IAM policy -ROLE = 'roles/owner' - - -def test_create_key_ring(capsys): - snippets.create_key_ring(PROJECT, LOCATION, KEY_RING) - out, _ = capsys.readouterr() - expected = 'Created KeyRing projects/{}/locations/{}/keyRings/{}.'.format( - PROJECT, LOCATION, KEY_RING) - assert expected in out - - -def test_create_crypto_key(capsys): - snippets.create_crypto_key( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY) - out, _ = capsys.readouterr() - expected = ( - 'Created CryptoKey projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}.' - .format(PROJECT, LOCATION, KEY_RING, CRYPTO_KEY)) - assert expected in out - - -def test_encrypt_decrypt(capsys, tmpdir): - # Write to a plaintext file. - tmpdir.join('in.txt').write('SampleText') - - # Construct temporary files. - plaintext_file = tmpdir.join('in.txt') - encrypted_file = tmpdir.join('out.txt') - decrypted_file = tmpdir.join('out2.txt') - - # Encrypt text and then decrypt it. - snippets.encrypt( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, - str(plaintext_file), str(encrypted_file)) - snippets.decrypt( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, - str(encrypted_file), str(decrypted_file)) - - # Make sure the decrypted text matches the original text. - decrypted_text = decrypted_file.read() - assert decrypted_text == 'SampleText' - - # Make sure other output is as expected. - out, _ = capsys.readouterr() - assert 'Saved ciphertext to {}.'.format(str(encrypted_file)) in out - assert 'Saved plaintext to {}.'.format(str(decrypted_file)) in out - - -def test_disable_crypto_key_version(capsys): - snippets.disable_crypto_key_version( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) - out, _ = capsys.readouterr() - expected = ( - 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}\'s state has been set to {}.' - .format( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, - 'DISABLED')) - assert expected in out - - -def test_enable_crypto_key_version(capsys): - snippets.enable_crypto_key_version( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) - out, _ = capsys.readouterr() - expected = ( - 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}\'s state has been set to {}.' - .format( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, - 'ENABLED')) - assert expected in out - - -def test_destroy_crypto_key_version(capsys): - snippets.destroy_crypto_key_version( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) - out, _ = capsys.readouterr() - expected = ( - 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}\'s state has been set to {}.' - .format( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, - 'DESTROY_SCHEDULED')) - assert expected in out - - -def test_restore_crypto_key_version(capsys): - snippets.restore_crypto_key_version( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION) - out, _ = capsys.readouterr() - expected = ( - 'CryptoKeyVersion projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}/' - 'cryptoKeyVersions/{}\'s state has been set to {}.' - .format( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, VERSION, - 'DISABLED')) - assert expected in out - - -def test_add_member_to_crypto_key_policy(capsys): - snippets.add_member_to_crypto_key_policy( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY, MEMBER, ROLE) - out, _ = capsys.readouterr() - expected = ( - 'Member {} added with role {} to policy for CryptoKey {} in KeyRing {}' - .format(MEMBER, ROLE, CRYPTO_KEY, KEY_RING)) - assert expected in out - - kms_client = googleapiclient.discovery.build('cloudkms', 'v1') - parent = 'projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}'.format( - PROJECT, LOCATION, KEY_RING, CRYPTO_KEY) - crypto_keys = kms_client.projects().locations().keyRings().cryptoKeys() - policy_request = crypto_keys.getIamPolicy(resource=parent) - policy_response = policy_request.execute() - assert 'bindings' in policy_response.keys() - bindings = policy_response['bindings'] - found_member_role_pair = False - for binding in bindings: - if binding['role'] == ROLE: - for user in binding['members']: - if user == MEMBER: - found_member_role_pair = True - assert found_member_role_pair - - -def test_get_key_ring_policy(capsys): - snippets.get_key_ring_policy(PROJECT, LOCATION, KEY_RING) - out, _ = capsys.readouterr() - expected_roles_exist = ( - 'Printing IAM policy for resource projects/{}/locations/{}/keyRings/{}' - ':'.format(PROJECT, LOCATION, KEY_RING)) - expected_no_roles = ( - 'No roles found for resource projects/{}/locations/{}/keyRings/{}.' - .format(PROJECT, LOCATION, KEY_RING)) - assert (expected_roles_exist in out) or (expected_no_roles in out) + +def create_key_helper(key_id, purpose, algorithm, t): + try: + client = kms_v1.KeyManagementServiceClient() + parent = client.key_ring_path(t.project_id, t.location, t.keyring_id) + + crypto_key = {'purpose': purpose, + 'version_template': {'algorithm': algorithm}} + client.create_crypto_key(parent, key_id, crypto_key) + return True + except GoogleAPICallError: + # key already exists + return False + + +def setup_module(module): + """ + Set up keys in project if needed + """ + t = TestKMSSnippets() + try: + # create keyring + snippets.create_key_ring(t.project_id, t.location, t.keyring_id) + except GoogleAPICallError: + # keyring already exists + pass + s = create_key_helper(t.symId, + enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. + GOOGLE_SYMMETRIC_ENCRYPTION, + t) + if s: + # leave time for key to initialize + time.sleep(20) + + +class TestKMSSnippets: + project_id = environ['GCLOUD_PROJECT'] + keyring_id = 'kms-samples' + location = 'global' + parent = 'projects/{}/locations/{}'.format(project_id, location) + keyring_path = '{}/keyRings/{}'.format(parent, keyring_id) + version = '1' + + symId = 'symmetric' + + sym = '{}/cryptoKeys/{}'.format(keyring_path, symId) + sym_version = '{}/cryptoKeyVersions/{}'.format(sym, version) + + message = 'test message 123' + message_bytes = message.encode('utf-8') + + member = 'group:test@google.com' + role = 'roles/viewer' + + @pytest.mark.skip(reason="There's currently no method to delete keyrings, \ + so we should avoid creating resources") + def test_create_key_ring(self): + ring_id = self.keyring_id + '-testcreate' + str(int(time.time())) + snippets.create_key_ring(self.project_id, self.location, ring_id) + client = kms_v1.KeyManagementServiceClient() + result = client.get_key_ring(client.key_ring_path(self.project_id, + self.location, + ring_id)) + assert ring_id in result.name + + @pytest.mark.skip(reason="Deleting keys isn't instant, so we should avoid \ + creating a large number of them in our tests") + def test_create_crypto_key(self): + key_id = self.symId + '-test' + str(int(time.time())) + snippets.create_crypto_key(self.project_id, self.location, + self.keyring_id, key_id) + c = kms_v1.KeyManagementServiceClient() + result = c.get_crypto_key(c.crypto_key_path(self.project_id, + self.location, + self.keyring_id, + key_id)) + assert key_id in result.name + + # tests disable/enable/destroy/restore + def test_key_change_version_state(self): + client = kms_v1.KeyManagementServiceClient() + name = client.crypto_key_version_path(self.project_id, self.location, + self.keyring_id, self.symId, + self.version) + state_enum = enums.CryptoKeyVersion.CryptoKeyVersionState + # test disable + snippets.disable_crypto_key_version(self.project_id, self.location, + self.keyring_id, self.symId, + self.version) + response = client.get_crypto_key_version(name) + assert response.state == state_enum.DISABLED + # test destroy + snippets.destroy_crypto_key_version(self.project_id, self.location, + self.keyring_id, self.symId, + self.version) + response = client.get_crypto_key_version(name) + assert response.state == state_enum.DESTROY_SCHEDULED + # test restore + snippets.restore_crypto_key_version(self.project_id, self.location, + self.keyring_id, self.symId, + self.version) + response = client.get_crypto_key_version(name) + assert response.state == state_enum.DISABLED + # test re-enable + snippets.enable_crypto_key_version(self.project_id, self.location, + self.keyring_id, self.symId, + self.version) + response = client.get_crypto_key_version(name) + assert response.state == state_enum.ENABLED + + def test_get_ring_policy(self): + policy = snippets.get_key_ring_policy(self.project_id, + self.location, self.keyring_id) + assert type(policy) is Policy + + # tests get/add/remove policy members + def test_ring_policy(self): + # add member + snippets.add_member_to_key_ring_policy(self.project_id, self.location, + self.keyring_id, self.member, + self.role) + policy = snippets.get_key_ring_policy(self.project_id, + self.location, self.keyring_id) + found = False + for b in list(policy.bindings): + if b.role == self.role and self.member in b.members: + found = True + assert found + # remove member + snippets.remove_member_from_key_ring_policy(self.project_id, + self.location, + self.keyring_id, + self.member, + self.role) + policy = snippets.get_key_ring_policy(self.project_id, + self.location, self.keyring_id) + found = False + for b in list(policy.bindings): + if b.role == self.role and self.member in b.members: + found = True + assert not found + + # tests get/add/remove policy members + def test_key_policy(self): + # add member + snippets.add_member_to_crypto_key_policy(self.project_id, + self.location, + self.keyring_id, + self.symId, + self.member, + self.role) + policy = snippets.get_crypto_key_policy(self.project_id, + self.location, + self.keyring_id, + self.symId) + found = False + for b in list(policy.bindings): + if b.role == self.role and self.member in b.members: + found = True + assert found + # remove member + snippets.remove_member_from_crypto_key_policy(self.project_id, + self.location, + self.keyring_id, + self.symId, + self.member, + self.role) + policy = snippets.get_crypto_key_policy(self.project_id, + self.location, + self.keyring_id, + self.symId) + found = False + for b in list(policy.bindings): + if b.role == self.role and self.member in b.members: + found = True + assert not found + + def test_symmetric_encrypt_decrypt(self): + cipher_bytes = snippets.encrypt_symmetric(self.project_id, + self.location, + self.keyring_id, + self.symId, + self.message_bytes) + plain_bytes = snippets.decrypt_symmetric(self.project_id, + self.location, + self.keyring_id, + self.symId, + cipher_bytes) + assert plain_bytes == self.message_bytes + assert cipher_bytes != self.message_bytes + plaintext = plain_bytes.decode("utf-8") + assert plaintext == self.message From 7b25fbcdc8fafeb10897ea2f1da8b1f0b2286bec Mon Sep 17 00:00:00 2001 From: DPEBot Date: Wed, 6 Feb 2019 12:06:35 -0800 Subject: [PATCH 33/52] Auto-update dependencies. [(#1980)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1980) * Auto-update dependencies. * Update requirements.txt * Update requirements.txt --- samples/snippets/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 74f7afd7..3c8d13c9 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-kms==0.2 -cryptography==2.4.2 +google-cloud-kms==0.2.1 +cryptography==2.5 From fe32c0c758ac515867eef51d6b05be392553ef06 Mon Sep 17 00:00:00 2001 From: Charles Engelke Date: Thu, 13 Jun 2019 14:20:59 -0700 Subject: [PATCH 34/52] Move import statements into tagged regions [(#2219)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/2219) * Move import statements into tagged regions So they will show up in context on web pages. * Flake8 didn't like needless enums imports I have to admit, it's right. It's clearer now. --- samples/snippets/snippets.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index 07b02705..d4209e6b 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -14,14 +14,12 @@ # See the License for the specific language governing permissions and -from google.cloud import kms_v1 -from google.cloud.kms_v1 import enums - - # [START kms_create_keyring] def create_key_ring(project_id, location_id, key_ring_id): """Creates a KeyRing in the given location (e.g. global).""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -44,6 +42,9 @@ def create_key_ring(project_id, location_id, key_ring_id): def create_crypto_key(project_id, location_id, key_ring_id, crypto_key_id): """Creates a CryptoKey within a KeyRing in the given location.""" + from google.cloud import kms_v1 + from google.cloud.kms_v1 import enums + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -67,6 +68,8 @@ def encrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, plaintext): """Encrypts input plaintext data using the provided symmetric CryptoKey.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -85,6 +88,8 @@ def decrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, ciphertext): """Decrypts input ciphertext using the provided symmetric CryptoKey.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -103,6 +108,9 @@ def disable_crypto_key_version(project_id, location_id, key_ring_id, """Disables a CryptoKeyVersion associated with a given CryptoKey and KeyRing.""" + from google.cloud import kms_v1 + from google.cloud.kms_v1 import enums + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -128,6 +136,9 @@ def enable_crypto_key_version(project_id, location_id, key_ring_id, """Enables a CryptoKeyVersion associated with a given CryptoKey and KeyRing.""" + from google.cloud import kms_v1 + from google.cloud.kms_v1 import enums + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -153,6 +164,8 @@ def destroy_crypto_key_version( """Schedules a CryptoKeyVersion associated with a given CryptoKey and KeyRing for destruction 24 hours in the future.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -174,6 +187,8 @@ def restore_crypto_key_version( project_id, location_id, key_ring_id, crypto_key_id, version_id): """Restores a CryptoKeyVersion that is scheduled for destruction.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -198,6 +213,8 @@ def add_member_to_crypto_key_policy( """Adds a member with a given role to the Identity and Access Management (IAM) policy for a given CryptoKey associated with a KeyRing.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -227,6 +244,8 @@ def add_member_to_key_ring_policy( """Adds a member with a given role to the Identity and Access Management (IAM) policy for a given KeyRing.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -257,6 +276,8 @@ def remove_member_from_crypto_key_policy( """Removes a member with a given role from the Identity and Access Management (IAM) policy for a given CryptoKey associated with a KeyRing.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -285,6 +306,8 @@ def remove_member_from_key_ring_policy(project_id, location_id, key_ring_id, """Removes a member with a given role from the Identity and Access Management (IAM) policy for a given KeyRing.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -312,6 +335,8 @@ def get_key_ring_policy(project_id, location_id, key_ring_id): """Gets the Identity and Access Management (IAM) policy for a given KeyRing and prints out roles and the members assigned to those roles.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() @@ -334,6 +359,8 @@ def get_crypto_key_policy(project_id, location_id, key_ring_id, crypto_key_id): """Gets the Identity and Access Management (IAM) policy for a given KeyRing and prints out roles and the members assigned to those roles.""" + from google.cloud import kms_v1 + # Creates an API client for the KMS API. client = kms_v1.KeyManagementServiceClient() From dc0df2282e95a33f430a68d6d960486488d67eec Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 8 Oct 2019 09:53:32 -0700 Subject: [PATCH 35/52] Adds split updates for Firebase ... opencensus [(#2438)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/2438) --- samples/snippets/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 3c8d13c9..efc7be5e 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-kms==0.2.1 -cryptography==2.5 +google-cloud-kms==1.2.1 +cryptography==2.7 From 18100cc89a8c62d681ffc4040445a4ae1efca3d3 Mon Sep 17 00:00:00 2001 From: DPEBot Date: Fri, 20 Dec 2019 17:41:38 -0800 Subject: [PATCH 36/52] Auto-update dependencies. [(#2005)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/2005) * Auto-update dependencies. * Revert update of appengine/flexible/datastore. * revert update of appengine/flexible/scipy * revert update of bigquery/bqml * revert update of bigquery/cloud-client * revert update of bigquery/datalab-migration * revert update of bigtable/quickstart * revert update of compute/api * revert update of container_registry/container_analysis * revert update of dataflow/run_template * revert update of datastore/cloud-ndb * revert update of dialogflow/cloud-client * revert update of dlp * revert update of functions/imagemagick * revert update of functions/ocr/app * revert update of healthcare/api-client/fhir * revert update of iam/api-client * revert update of iot/api-client/gcs_file_to_device * revert update of iot/api-client/mqtt_example * revert update of language/automl * revert update of run/image-processing * revert update of vision/automl * revert update testing/requirements.txt * revert update of vision/cloud-client/detect * revert update of vision/cloud-client/product_search * revert update of jobs/v2/api_client * revert update of jobs/v3/api_client * revert update of opencensus * revert update of translate/cloud-client * revert update to speech/cloud-client Co-authored-by: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Co-authored-by: Doug Mahugh --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index efc7be5e..a9b47ef6 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ google-cloud-kms==1.2.1 -cryptography==2.7 +cryptography==2.8 From 17f78cff87e96155bbf3315cc2cf22723045b3da Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 30 Mar 2020 20:48:20 +0200 Subject: [PATCH 37/52] chore(deps): update dependency google-cloud-kms to v1.3.0 [(#3160)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3160) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index a9b47ef6..81377367 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-kms==1.2.1 +google-cloud-kms==1.3.0 cryptography==2.8 From a4df66a8cb6830d0319b17ad247d7b088a46a785 Mon Sep 17 00:00:00 2001 From: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Date: Wed, 1 Apr 2020 19:11:50 -0700 Subject: [PATCH 38/52] Simplify noxfile setup. [(#2806)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/2806) * chore(deps): update dependency requests to v2.23.0 * Simplify noxfile and add version control. * Configure appengine/standard to only test Python 2.7. * Update Kokokro configs to match noxfile. * Add requirements-test to each folder. * Remove Py2 versions from everything execept appengine/standard. * Remove conftest.py. * Remove appengine/standard/conftest.py * Remove 'no-sucess-flaky-report' from pytest.ini. * Add GAE SDK back to appengine/standard tests. * Fix typo. * Roll pytest to python 2 version. * Add a bunch of testing requirements. * Remove typo. * Add appengine lib directory back in. * Add some additional requirements. * Fix issue with flake8 args. * Even more requirements. * Readd appengine conftest.py. * Add a few more requirements. * Even more Appengine requirements. * Add webtest for appengine/standard/mailgun. * Add some additional requirements. * Add workaround for issue with mailjet-rest. * Add responses for appengine/standard/mailjet. Co-authored-by: Renovate Bot --- samples/snippets/quickstart_test.py | 2 +- samples/snippets/requirements-test.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 samples/snippets/requirements-test.txt diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py index 0db901e6..5a457518 100644 --- a/samples/snippets/quickstart_test.py +++ b/samples/snippets/quickstart_test.py @@ -13,7 +13,7 @@ # limitations under the License. -def test_quickstart(api_client_inject_project_id): +def test_quickstart(): import quickstart quickstart.run_quickstart() diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt new file mode 100644 index 00000000..781d4326 --- /dev/null +++ b/samples/snippets/requirements-test.txt @@ -0,0 +1 @@ +pytest==5.3.2 From e1fe4daf51e6a31d42fac51867ef12b2210683ff Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 7 Apr 2020 11:43:56 -0700 Subject: [PATCH 39/52] [kms] fix flaky test [(#3268)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3268) --- samples/snippets/requirements-test.txt | 2 + samples/snippets/snippets_test.py | 99 +++++++++++++++----------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 781d4326..758bc040 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1 +1,3 @@ pytest==5.3.2 +gcp-devrel-py-tools==0.0.15 +google-cloud-core diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index 9da03d9c..9b2c7ad4 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -16,7 +16,7 @@ import time from os import environ -from google.api_core.exceptions import GoogleAPICallError +from google.api_core.exceptions import Aborted, GoogleAPICallError from google.cloud import kms_v1 from google.cloud.kms_v1 import enums from google.iam.v1.policy_pb2 import Policy @@ -25,6 +25,8 @@ import snippets +from gcp_devrel.testing import eventually_consistent + def create_key_helper(key_id, purpose, algorithm, t): try: @@ -51,7 +53,7 @@ def setup_module(module): except GoogleAPICallError: # keyring already exists pass - s = create_key_helper(t.symId, + s = create_key_helper(t.sym_id, enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. GOOGLE_SYMMETRIC_ENCRYPTION, @@ -69,9 +71,9 @@ class TestKMSSnippets: keyring_path = '{}/keyRings/{}'.format(parent, keyring_id) version = '1' - symId = 'symmetric' + sym_id = 'symmetric' - sym = '{}/cryptoKeys/{}'.format(keyring_path, symId) + sym = '{}/cryptoKeys/{}'.format(keyring_path, sym_id) sym_version = '{}/cryptoKeyVersions/{}'.format(sym, version) message = 'test message 123' @@ -94,7 +96,7 @@ def test_create_key_ring(self): @pytest.mark.skip(reason="Deleting keys isn't instant, so we should avoid \ creating a large number of them in our tests") def test_create_crypto_key(self): - key_id = self.symId + '-test' + str(int(time.time())) + key_id = self.sym_id + '-test' + str(int(time.time())) snippets.create_crypto_key(self.project_id, self.location, self.keyring_id, key_id) c = kms_v1.KeyManagementServiceClient() @@ -108,30 +110,30 @@ def test_create_crypto_key(self): def test_key_change_version_state(self): client = kms_v1.KeyManagementServiceClient() name = client.crypto_key_version_path(self.project_id, self.location, - self.keyring_id, self.symId, + self.keyring_id, self.sym_id, self.version) state_enum = enums.CryptoKeyVersion.CryptoKeyVersionState # test disable snippets.disable_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.symId, + self.keyring_id, self.sym_id, self.version) response = client.get_crypto_key_version(name) assert response.state == state_enum.DISABLED # test destroy snippets.destroy_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.symId, + self.keyring_id, self.sym_id, self.version) response = client.get_crypto_key_version(name) assert response.state == state_enum.DESTROY_SCHEDULED # test restore snippets.restore_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.symId, + self.keyring_id, self.sym_id, self.version) response = client.get_crypto_key_version(name) assert response.state == state_enum.DISABLED # test re-enable snippets.enable_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.symId, + self.keyring_id, self.sym_id, self.version) response = client.get_crypto_key_version(name) assert response.state == state_enum.ENABLED @@ -171,48 +173,61 @@ def test_ring_policy(self): # tests get/add/remove policy members def test_key_policy(self): # add member - snippets.add_member_to_crypto_key_policy(self.project_id, - self.location, - self.keyring_id, - self.symId, - self.member, - self.role) - policy = snippets.get_crypto_key_policy(self.project_id, - self.location, - self.keyring_id, - self.symId) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert found + snippets.add_member_to_crypto_key_policy( + self.project_id, + self.location, + self.keyring_id, + self.sym_id, + self.member, + self.role) + + def check_policy(): + policy = snippets.get_crypto_key_policy( + self.project_id, + self.location, + self.keyring_id, + self.sym_id) + found = False + for b in list(policy.bindings): + if b.role == self.role and self.member in b.members: + found = True + assert found + eventually_consistent.call(check_policy, + exceptions=(Aborted, AssertionError)) # remove member - snippets.remove_member_from_crypto_key_policy(self.project_id, - self.location, - self.keyring_id, - self.symId, - self.member, - self.role) - policy = snippets.get_crypto_key_policy(self.project_id, - self.location, - self.keyring_id, - self.symId) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert not found + snippets.remove_member_from_crypto_key_policy( + self.project_id, + self.location, + self.keyring_id, + self.sym_id, + self.member, + self.role) + + def check_policy(): + policy = snippets.get_crypto_key_policy( + self.project_id, + self.location, + self.keyring_id, + self.sym_id) + found = False + for b in list(policy.bindings): + if b.role == self.role and self.member in b.members: + found = True + assert not found + eventually_consistent.call( + check_policy, + exceptions=(Aborted, AssertionError)) def test_symmetric_encrypt_decrypt(self): cipher_bytes = snippets.encrypt_symmetric(self.project_id, self.location, self.keyring_id, - self.symId, + self.sym_id, self.message_bytes) plain_bytes = snippets.decrypt_symmetric(self.project_id, self.location, self.keyring_id, - self.symId, + self.sym_id, cipher_bytes) assert plain_bytes == self.message_bytes assert cipher_bytes != self.message_bytes From b376b7b88da0be82b8d8e833fef54f4f209c06f9 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Tue, 21 Apr 2020 03:44:04 +0200 Subject: [PATCH 40/52] Update dependency google-cloud-kms to v1.4.0 [(#3410)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3410) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [google-cloud-kms](https://togithub.com/googleapis/python-kms) | minor | `==1.3.0` -> `==1.4.0` | --- ### Release Notes
googleapis/python-kms ### [`v1.4.0`](https://togithub.com/googleapis/python-kms/blob/master/CHANGELOG.md#​140-httpswwwgithubcomgoogleapispython-kmscomparev130v140-2020-04-14) [Compare Source](https://togithub.com/googleapis/python-kms/compare/v1.3.0...v1.4.0) ##### Features - add support for external key manager (via synth) ([#​8](https://www.github.com/googleapis/python-kms/issues/8)) ([4077fc8](https://www.github.com/googleapis/python-kms/commit/4077fc89943cc09d489d44c05efcf9cab61cdbaf))
--- ### Renovate configuration :date: **Schedule**: At any time (no schedule defined). :vertical_traffic_light: **Automerge**: Disabled by config. Please merge this manually once you are satisfied. :recycle: **Rebasing**: Never, or you tick the rebase/retry checkbox. :no_bell: **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#GoogleCloudPlatform/python-docs-samples). --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 81377367..c10519b9 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ -google-cloud-kms==1.3.0 +google-cloud-kms==1.4.0 cryptography==2.8 From 5581e83e3f7d302992c566ece113ca690ebf323c Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 22 Apr 2020 06:14:10 +0200 Subject: [PATCH 41/52] chore(deps): update dependency cryptography to v2.9 [(#3266)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3266) Co-authored-by: Leah E. Cole <6719667+leahecole@users.noreply.github.com> --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index c10519b9..aaf2ea5f 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ google-cloud-kms==1.4.0 -cryptography==2.8 +cryptography==2.9 From b33aa1c3f31abccb7d4fce03d7fd51410470cb76 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Date: Wed, 22 Apr 2020 09:16:04 -0700 Subject: [PATCH 42/52] fix: use `crypto_key_path` instead of `crypto_key_path_path` [(#3319)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3319) `crypto_key_path_path` was added to the library in error and will be removed in a future version of the library --- samples/snippets/snippets.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py index d4209e6b..e09a3a79 100644 --- a/samples/snippets/snippets.py +++ b/samples/snippets/snippets.py @@ -74,8 +74,7 @@ def encrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - name = client.crypto_key_path_path(project_id, location_id, key_ring_id, - crypto_key_id) + name = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) # Use the KMS API to encrypt the data. response = client.encrypt(name, plaintext) @@ -94,8 +93,7 @@ def decrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - name = client.crypto_key_path_path(project_id, location_id, key_ring_id, - crypto_key_id) + name = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) # Use the KMS API to decrypt the data. response = client.decrypt(name, ciphertext) return response.plaintext @@ -219,8 +217,7 @@ def add_member_to_crypto_key_policy( client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - resource = client.crypto_key_path_path(project_id, location_id, - key_ring_id, crypto_key_id) + resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) # Get the current IAM policy. policy = client.get_iam_policy(resource) @@ -282,8 +279,8 @@ def remove_member_from_crypto_key_policy( client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - resource = client.crypto_key_path_path(project_id, location_id, - key_ring_id, crypto_key_id) + resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) + # Get the current IAM policy. policy = client.get_iam_policy(resource) @@ -365,8 +362,7 @@ def get_crypto_key_policy(project_id, location_id, key_ring_id, crypto_key_id): client = kms_v1.KeyManagementServiceClient() # The resource name of the CryptoKey. - resource = client.crypto_key_path_path(project_id, location_id, - key_ring_id, crypto_key_id) + resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) # Get the current IAM policy. policy = client.get_iam_policy(resource) From 1e095c3614405527dd1048cb898cdb02b6682d51 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 22 Apr 2020 21:07:11 +0200 Subject: [PATCH 43/52] chore(deps): update dependency cryptography to v2.9.1 [(#3463)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3463) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index aaf2ea5f..19a906ab 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ google-cloud-kms==1.4.0 -cryptography==2.9 +cryptography==2.9.1 From aa5277bba800e526820f57f698a01b017a193e8a Mon Sep 17 00:00:00 2001 From: Takashi Matsuo Date: Thu, 23 Apr 2020 10:36:06 -0700 Subject: [PATCH 44/52] [kms] chore: remove gcp-devrel-py-tools [(#3479)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3479) --- samples/snippets/requirements-test.txt | 3 +-- samples/snippets/snippets_test.py | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 758bc040..8855f3cf 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,3 +1,2 @@ +backoff==1.10.0 pytest==5.3.2 -gcp-devrel-py-tools==0.0.15 -google-cloud-core diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index 9b2c7ad4..c5c34a28 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -20,13 +20,11 @@ from google.cloud import kms_v1 from google.cloud.kms_v1 import enums from google.iam.v1.policy_pb2 import Policy - +import backoff import pytest import snippets -from gcp_devrel.testing import eventually_consistent - def create_key_helper(key_id, purpose, algorithm, t): try: @@ -181,6 +179,8 @@ def test_key_policy(self): self.member, self.role) + @backoff.on_exception( + backoff.expo, (Aborted, AssertionError), max_time=60) def check_policy(): policy = snippets.get_crypto_key_policy( self.project_id, @@ -192,8 +192,9 @@ def check_policy(): if b.role == self.role and self.member in b.members: found = True assert found - eventually_consistent.call(check_policy, - exceptions=(Aborted, AssertionError)) + + check_policy() + # remove member snippets.remove_member_from_crypto_key_policy( self.project_id, @@ -203,7 +204,9 @@ def check_policy(): self.member, self.role) - def check_policy(): + @backoff.on_exception( + backoff.expo, (Aborted, AssertionError), max_time=60) + def check_policy_again(): policy = snippets.get_crypto_key_policy( self.project_id, self.location, @@ -214,9 +217,8 @@ def check_policy(): if b.role == self.role and self.member in b.members: found = True assert not found - eventually_consistent.call( - check_policy, - exceptions=(Aborted, AssertionError)) + + check_policy_again() def test_symmetric_encrypt_decrypt(self): cipher_bytes = snippets.encrypt_symmetric(self.project_id, From 6283510a18b01e41a409a72af2148dff2b058d2b Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 23 Apr 2020 22:47:42 +0200 Subject: [PATCH 45/52] Update dependency cryptography to v2.9.2 [(#3473)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3473) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 19a906ab..6e2cc2a4 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,2 +1,2 @@ google-cloud-kms==1.4.0 -cryptography==2.9.1 +cryptography==2.9.2 From 2f018a3414ec1e43bf78911055aa821790247cdc Mon Sep 17 00:00:00 2001 From: Takashi Matsuo Date: Mon, 27 Apr 2020 17:31:46 -0700 Subject: [PATCH 46/52] [kms] fix: use unique ids for test [(#3563)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3563) fixes #3554 fixes #3555 --- samples/snippets/snippets_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index c5c34a28..0b03bcda 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -15,6 +15,7 @@ import time from os import environ +import uuid from google.api_core.exceptions import Aborted, GoogleAPICallError from google.cloud import kms_v1 @@ -63,7 +64,7 @@ def setup_module(module): class TestKMSSnippets: project_id = environ['GCLOUD_PROJECT'] - keyring_id = 'kms-samples' + keyring_id = 'kms-samples-{}'.format(uuid.uuid4().hex) location = 'global' parent = 'projects/{}/locations/{}'.format(project_id, location) keyring_path = '{}/keyRings/{}'.format(parent, keyring_id) @@ -83,7 +84,7 @@ class TestKMSSnippets: @pytest.mark.skip(reason="There's currently no method to delete keyrings, \ so we should avoid creating resources") def test_create_key_ring(self): - ring_id = self.keyring_id + '-testcreate' + str(int(time.time())) + ring_id = self.keyring_id + '-test-create-{}'.format(uuid.uuid4().hex) snippets.create_key_ring(self.project_id, self.location, ring_id) client = kms_v1.KeyManagementServiceClient() result = client.get_key_ring(client.key_ring_path(self.project_id, @@ -94,7 +95,7 @@ def test_create_key_ring(self): @pytest.mark.skip(reason="Deleting keys isn't instant, so we should avoid \ creating a large number of them in our tests") def test_create_crypto_key(self): - key_id = self.sym_id + '-test' + str(int(time.time())) + key_id = self.sym_id + '-test-{}'.format(uuid.uuid4().hex) snippets.create_crypto_key(self.project_id, self.location, self.keyring_id, key_id) c = kms_v1.KeyManagementServiceClient() From 97d80d52c0ac1a78326f7d21e0e728bbd8bb4cfc Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Tue, 5 May 2020 18:46:19 -0400 Subject: [PATCH 47/52] Update and add Cloud KMS samples [(#3690)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3690) This updates the Cloud KMS samples to match the format from the other 6 languages. It also updates the samples to note the workaround for https://github.com/googleapis/gapic-generator-python/issues/364. --- samples/snippets/README.rst | 16 +- samples/snippets/README.rst.in | 5 - samples/snippets/asymmetric.py | 219 ------- samples/snippets/asymmetric_test.py | 133 ---- .../snippets/create_key_asymmetric_decrypt.py | 54 ++ .../snippets/create_key_asymmetric_sign.py | 54 ++ samples/snippets/create_key_hsm.py | 56 ++ samples/snippets/create_key_labels.py | 58 ++ samples/snippets/create_key_ring.py | 46 ++ .../snippets/create_key_rotation_schedule.py | 67 ++ .../create_key_symmetric_encrypt_decrypt.py | 54 ++ samples/snippets/create_key_version.py | 47 ++ samples/snippets/decrypt_asymmetric.py | 46 ++ samples/snippets/decrypt_symmetric.py | 45 ++ samples/snippets/destroy_key_version.py | 45 ++ samples/snippets/disable_key_version.py | 55 ++ samples/snippets/enable_key_version.py | 55 ++ samples/snippets/encrypt_asymmetric.py | 69 ++ samples/snippets/encrypt_symmetric.py | 51 ++ samples/snippets/get_key_labels.py | 48 ++ .../snippets/get_key_version_attestation.py | 56 ++ samples/snippets/get_public_key.py | 45 ++ samples/snippets/iam_add_member.py | 56 ++ samples/snippets/iam_get_policy.py | 54 ++ samples/snippets/iam_remove_member.py | 57 ++ samples/snippets/quickstart.py | 52 +- samples/snippets/quickstart_test.py | 19 - samples/snippets/requirements-test.txt | 3 +- samples/snippets/restore_key_version.py | 45 ++ samples/snippets/sign_asymmetric.py | 64 ++ samples/snippets/snippets.py | 375 ----------- samples/snippets/snippets_test.py | 619 ++++++++++++------ samples/snippets/update_key_add_rotation.py | 62 ++ samples/snippets/update_key_remove_labels.py | 54 ++ .../snippets/update_key_remove_rotation.py | 53 ++ samples/snippets/update_key_set_primary.py | 45 ++ samples/snippets/update_key_update_labels.py | 54 ++ samples/snippets/verify_asymmetric_ec.py | 72 ++ samples/snippets/verify_asymmetric_rsa.py | 73 +++ 39 files changed, 2069 insertions(+), 1012 deletions(-) delete mode 100644 samples/snippets/asymmetric.py delete mode 100644 samples/snippets/asymmetric_test.py create mode 100644 samples/snippets/create_key_asymmetric_decrypt.py create mode 100644 samples/snippets/create_key_asymmetric_sign.py create mode 100644 samples/snippets/create_key_hsm.py create mode 100644 samples/snippets/create_key_labels.py create mode 100644 samples/snippets/create_key_ring.py create mode 100644 samples/snippets/create_key_rotation_schedule.py create mode 100644 samples/snippets/create_key_symmetric_encrypt_decrypt.py create mode 100644 samples/snippets/create_key_version.py create mode 100644 samples/snippets/decrypt_asymmetric.py create mode 100644 samples/snippets/decrypt_symmetric.py create mode 100644 samples/snippets/destroy_key_version.py create mode 100644 samples/snippets/disable_key_version.py create mode 100644 samples/snippets/enable_key_version.py create mode 100644 samples/snippets/encrypt_asymmetric.py create mode 100644 samples/snippets/encrypt_symmetric.py create mode 100644 samples/snippets/get_key_labels.py create mode 100644 samples/snippets/get_key_version_attestation.py create mode 100644 samples/snippets/get_public_key.py create mode 100644 samples/snippets/iam_add_member.py create mode 100644 samples/snippets/iam_get_policy.py create mode 100644 samples/snippets/iam_remove_member.py delete mode 100644 samples/snippets/quickstart_test.py create mode 100644 samples/snippets/restore_key_version.py create mode 100644 samples/snippets/sign_asymmetric.py delete mode 100644 samples/snippets/snippets.py create mode 100644 samples/snippets/update_key_add_rotation.py create mode 100644 samples/snippets/update_key_remove_labels.py create mode 100644 samples/snippets/update_key_remove_rotation.py create mode 100644 samples/snippets/update_key_set_primary.py create mode 100644 samples/snippets/update_key_update_labels.py create mode 100644 samples/snippets/verify_asymmetric_ec.py create mode 100644 samples/snippets/verify_asymmetric_rsa.py diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 17e34bef..3acb00f5 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -77,20 +77,6 @@ To run this sample: $ python quickstart.py -Snippets -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/snippets.py,kms/api-client/README.rst - - - - -To run this sample: - -.. code-block:: bash - - $ python snippets.py -.. _Google Cloud SDK: https://cloud.google.com/sdk/ +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/samples/snippets/README.rst.in b/samples/snippets/README.rst.in index f8aef3a2..cfd81fc8 100644 --- a/samples/snippets/README.rst.in +++ b/samples/snippets/README.rst.in @@ -15,10 +15,5 @@ setup: samples: - name: Quickstart file: quickstart.py -- name: Snippets - file: snippets.py - show_help: True -- name: Asymmetric - file: asymmetric.py folder: kms/api-client diff --git a/samples/snippets/asymmetric.py b/samples/snippets/asymmetric.py deleted file mode 100644 index 9c8abd15..00000000 --- a/samples/snippets/asymmetric.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/bin/python -# Copyright 2018 Google Inc. -# -# 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.rom googleapiclient import discovery - -import hashlib - -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ec, padding, utils - -from google.cloud import kms_v1 -from google.cloud.kms_v1 import enums - - -# [START kms_create_asymmetric_key] -def create_asymmetric_key(project_id, location_id, key_ring_id, crypto_key_id): - """Creates an RSA encrypt/decrypt key pair within a specified KeyRing.""" - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing associated with the CryptoKey. - parent = client.key_ring_path(project_id, location_id, key_ring_id) - - # Create the CryptoKey object template - purpose = enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT - algorithm = enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.\ - RSA_DECRYPT_OAEP_2048_SHA256 - crypto_key = {'purpose': purpose, - 'version_template': {'algorithm': algorithm}} - - # Create a CryptoKey for the given KeyRing. - response = client.create_crypto_key(parent, crypto_key_id, crypto_key) - - print('Created CryptoKey {}.'.format(response.name)) - return response -# [END kms_create_asymmetric_key] - - -# [START kms_get_asymmetric_public] -def get_asymmetric_public_key(key_name): - """ - Retrieves the public key from a saved asymmetric key pair on Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.hazmat.backends.default_backend - cryptography.hazmat.primitives.serialization - """ - - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - - key_txt = response.pem.encode('ascii') - key = serialization.load_pem_public_key(key_txt, default_backend()) - return key -# [END kms_get_asymmetric_public] - - -# [START kms_decrypt_rsa] -def decrypt_rsa(ciphertext, key_name): - """ - Decrypt the input ciphertext (bytes) using an - 'RSA_DECRYPT_OAEP_2048_SHA256' private key stored on Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - """ - - client = kms_v1.KeyManagementServiceClient() - response = client.asymmetric_decrypt(key_name, ciphertext) - return response.plaintext -# [END kms_decrypt_rsa] - - -# [START kms_encrypt_rsa] -def encrypt_rsa(plaintext, key_name): - """ - Encrypt the input plaintext (bytes) locally using an - 'RSA_DECRYPT_OAEP_2048_SHA256' public key retrieved from Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.hazmat.primitives.asymmetric.padding - cryptography.hazmat.primitives.hashes - """ - # get the public key - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - key_txt = response.pem.encode('ascii') - public_key = serialization.load_pem_public_key(key_txt, default_backend()) - - # encrypt plaintext - pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None) - return public_key.encrypt(plaintext, pad) -# [END kms_encrypt_rsa] - - -# [START kms_sign_asymmetric] -def sign_asymmetric(message, key_name): - """ - Create a signature for a message using a private key stored on Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - hashlib - """ - # Note: some key algorithms will require a different hash function - # For example, EC_SIGN_P384_SHA384 requires SHA384 - client = kms_v1.KeyManagementServiceClient() - digest_bytes = hashlib.sha256(message).digest() - - digest_json = {'sha256': digest_bytes} - - response = client.asymmetric_sign(key_name, digest_json) - return response.signature -# [END kms_sign_asymmetric] - - -# [START kms_verify_signature_rsa] -def verify_signature_rsa(signature, message, key_name): - """ - Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the - specified message - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.exceptions.InvalidSignature - cryptography.hazmat.primitives.asymmetric.padding - cryptography.hazmat.primitives.asymmetric.utils - cryptography.hazmat.primitives.hashes - hashlib - """ - # get the public key - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - key_txt = response.pem.encode('ascii') - public_key = serialization.load_pem_public_key(key_txt, default_backend()) - - # get the digest of the message - digest_bytes = hashlib.sha256(message).digest() - - try: - # Attempt verification - public_key.verify(signature, - digest_bytes, - padding.PSS(mgf=padding.MGF1(hashes.SHA256()), - salt_length=32), - utils.Prehashed(hashes.SHA256())) - # No errors were thrown. Verification was successful - return True - except InvalidSignature: - return False -# [END kms_verify_signature_rsa] - - -# [START kms_verify_signature_ec] -def verify_signature_ec(signature, message, key_name): - """ - Verify the validity of an 'EC_SIGN_P256_SHA256' signature - for the specified message - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.exceptions.InvalidSignature - cryptography.hazmat.primitives.asymmetric.ec - cryptography.hazmat.primitives.asymmetric.utils - cryptography.hazmat.primitives.hashes - hashlib - """ - # get the public key - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - key_txt = response.pem.encode('ascii') - public_key = serialization.load_pem_public_key(key_txt, default_backend()) - - # get the digest of the message - digest_bytes = hashlib.sha256(message).digest() - - try: - # Attempt verification - public_key.verify(signature, - digest_bytes, - ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - # No errors were thrown. Verification was successful - return True - except InvalidSignature: - return False -# [END kms_verify_signature_ec] diff --git a/samples/snippets/asymmetric_test.py b/samples/snippets/asymmetric_test.py deleted file mode 100644 index cc621003..00000000 --- a/samples/snippets/asymmetric_test.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/python -# Copyright 2018 Google Inc. -# -# 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. - -from os import environ -from time import sleep - -import asymmetric - -from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePublicKey -from cryptography.hazmat.backends.openssl.rsa import _RSAPublicKey - -from google.api_core.exceptions import GoogleAPICallError -from google.cloud.kms_v1 import enums - -from snippets import create_key_ring - -from snippets_test import create_key_helper - - -def setup_module(module): - """ - Set up keys in project if needed - """ - t = TestKMSAsymmetric() - try: - # create keyring - create_key_ring(t.project_id, t.location, t.keyring_id) - except GoogleAPICallError: - # keyring already exists - pass - s1 = create_key_helper(t.rsaDecryptId, - enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - RSA_DECRYPT_OAEP_2048_SHA256, - t) - s2 = create_key_helper(t.rsaSignId, - enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - RSA_SIGN_PSS_2048_SHA256, - t) - s3 = create_key_helper(t.ecSignId, - enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - EC_SIGN_P256_SHA256, - t) - - if s1 or s2 or s3: - # leave time for keys to initialize - sleep(20) - - -class TestKMSAsymmetric: - project_id = environ['GCLOUD_PROJECT'] - keyring_id = 'kms-samples' - location = 'global' - parent = 'projects/{}/locations/{}'.format(project_id, location) - - rsaSignId = 'rsa-sign' - rsaDecryptId = 'rsa-decrypt' - ecSignId = 'ec-sign' - - rsaSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring_id, rsaSignId) - rsaDecrypt = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring_id, rsaDecryptId) - ecSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring_id, ecSignId) - - message = 'test message 123' - message_bytes = message.encode('utf-8') - - def test_get_public_key(self): - rsa_key = asymmetric.get_asymmetric_public_key(self.rsaDecrypt) - assert isinstance(rsa_key, _RSAPublicKey), 'expected RSA key' - rsa_key = asymmetric.get_asymmetric_public_key(self.rsaSign) - assert isinstance(rsa_key, _RSAPublicKey), 'expected RSA key' - ec_key = asymmetric.get_asymmetric_public_key(self.ecSign) - assert isinstance(ec_key, _EllipticCurvePublicKey), 'expected EC key' - - def test_rsa_encrypt_decrypt(self): - ciphertext = asymmetric.encrypt_rsa(self.message_bytes, - self.rsaDecrypt) - # signature should be 256 bytes for RSA 2048 - assert len(ciphertext) == 256, \ - 'ciphertext should be 256 chars; got {}'.format(len(ciphertext)) - plaintext_bytes = asymmetric.decrypt_rsa(ciphertext, - self.rsaDecrypt) - assert plaintext_bytes == self.message_bytes - plaintext = plaintext_bytes.decode('utf-8') - assert plaintext == self.message - - def test_rsa_sign_verify(self): - sig = asymmetric.sign_asymmetric(self.message_bytes, - self.rsaSign) - # signature should be 256 bytes for RSA 2048 - assert len(sig) == 256, \ - 'sig should be 256 chars; got {}'.format(len(sig)) - success = asymmetric.verify_signature_rsa(sig, - self.message_bytes, - self.rsaSign) - assert success is True, 'RSA verification failed' - changed_bytes = self.message_bytes + b'.' - success = asymmetric.verify_signature_rsa(sig, - changed_bytes, - self.rsaSign) - assert success is False, 'verify should fail with modified message' - - def test_ec_sign_verify(self): - sig = asymmetric.sign_asymmetric(self.message_bytes, - self.ecSign) - assert len(sig) > 50 and len(sig) < 300, \ - 'sig outside expected length range' - success = asymmetric.verify_signature_ec(sig, - self.message_bytes, - self.ecSign) - assert success is True, 'EC verification failed' - changed_bytes = self.message_bytes + b'.' - success = asymmetric.verify_signature_ec(sig, - changed_bytes, - self.ecSign) - assert success is False, 'verify should fail with modified message' diff --git a/samples/snippets/create_key_asymmetric_decrypt.py b/samples/snippets/create_key_asymmetric_decrypt.py new file mode 100644 index 00000000..cac15795 --- /dev/null +++ b/samples/snippets/create_key_asymmetric_decrypt.py @@ -0,0 +1,54 @@ +# Copyright 2020 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 + + +# [START kms_create_key_asymmetric_decrypt] +def create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, id): + """ + Creates a new asymmetric decryption key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-asymmetric-decrypt-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created asymmetric decrypt key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_asymmetric_decrypt] diff --git a/samples/snippets/create_key_asymmetric_sign.py b/samples/snippets/create_key_asymmetric_sign.py new file mode 100644 index 00000000..9bf18a7a --- /dev/null +++ b/samples/snippets/create_key_asymmetric_sign.py @@ -0,0 +1,54 @@ +# Copyright 2020 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 + + +# [START kms_create_key_asymmetric_sign] +def create_key_asymmetric_sign(project_id, location_id, key_ring_id, id): + """ + Creates a new asymmetric signing key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-asymmetric-signing-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created asymmetric signing key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_asymmetric_sign] diff --git a/samples/snippets/create_key_hsm.py b/samples/snippets/create_key_hsm.py new file mode 100644 index 00000000..84ba37e5 --- /dev/null +++ b/samples/snippets/create_key_hsm.py @@ -0,0 +1,56 @@ +# Copyright 2020 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 + + +# [START kms_create_key_hsm] +def create_key_hsm(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS backed by Cloud HSM. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-hsm-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + protection_level = kms.enums.ProtectionLevel.HSM + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + 'protection_level': protection_level + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created hsm key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_hsm] diff --git a/samples/snippets/create_key_labels.py b/samples/snippets/create_key_labels.py new file mode 100644 index 00000000..e64a10cb --- /dev/null +++ b/samples/snippets/create_key_labels.py @@ -0,0 +1,58 @@ +# Copyright 2020 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 + + +# [START kms_create_key_labels] +def create_key_labels(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS with labels. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-labeled-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + }, + 'labels': { + 'team': 'alpha', + 'cost_center': 'cc1234' + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created labeled key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_labels] diff --git a/samples/snippets/create_key_ring.py b/samples/snippets/create_key_ring.py new file mode 100644 index 00000000..c01e8490 --- /dev/null +++ b/samples/snippets/create_key_ring.py @@ -0,0 +1,46 @@ +# Copyright 2020 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 + + +# [START kms_create_key_ring] +def create_key_ring(project_id, location_id, id): + """ + Creates a new key ring in Cloud KMS + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + id (string): ID of the key ring to create (e.g. 'my-key-ring'). + + Returns: + KeyRing: Cloud KMS key ring. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent location name. + location_name = client.location_path(project_id, location_id) + + # Build the key ring. + key_ring = {} + + # Call the API. + created_key_ring = client.create_key_ring(location_name, id, key_ring) + print('Created key ring: {}'.format(created_key_ring.name)) + return created_key_ring +# [END kms_create_key_ring] diff --git a/samples/snippets/create_key_rotation_schedule.py b/samples/snippets/create_key_rotation_schedule.py new file mode 100644 index 00000000..e6bbdb62 --- /dev/null +++ b/samples/snippets/create_key_rotation_schedule.py @@ -0,0 +1,67 @@ +# Copyright 2020 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 + + +# [START kms_create_key_rotation_schedule] +def create_key_rotation_schedule(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS that automatically rotates. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-rotating-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Import time for getting the current time. + import time + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + }, + + # Rotate the key every 30 days. + 'rotation_period': { + 'seconds': 60*60*24*30 + }, + + # Start the first rotation in 24 hours. + 'next_rotation_time': { + 'seconds': int(time.time()) + 60*60*24 + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created labeled key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_rotation_schedule] diff --git a/samples/snippets/create_key_symmetric_encrypt_decrypt.py b/samples/snippets/create_key_symmetric_encrypt_decrypt.py new file mode 100644 index 00000000..54b9c5f4 --- /dev/null +++ b/samples/snippets/create_key_symmetric_encrypt_decrypt.py @@ -0,0 +1,54 @@ +# Copyright 2020 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 + + +# [START kms_create_key_symmetric_encrypt_decrypt] +def create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id, id): + """ + Creates a new symmetric encryption/decryption key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-symmetric-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created symmetric key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_symmetric_encrypt_decrypt] diff --git a/samples/snippets/create_key_version.py b/samples/snippets/create_key_version.py new file mode 100644 index 00000000..9c84f808 --- /dev/null +++ b/samples/snippets/create_key_version.py @@ -0,0 +1,47 @@ +# Copyright 2020 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 + + +# [START kms_create_key_version] +def create_key_version(project_id, location_id, key_ring_id, key_id): + """ + Creates a new version of the given key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key for which to create a new version (e.g. 'my-key'). + + Returns: + CryptoKeyVersion: Cloud KMS key version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key version. + version = {} + + # Call the API. + created_version = client.create_crypto_key_version(key_name, version) + print('Created key version: {}'.format(created_version.name)) + return created_version +# [END kms_create_key_version] diff --git a/samples/snippets/decrypt_asymmetric.py b/samples/snippets/decrypt_asymmetric.py new file mode 100644 index 00000000..7b040cdd --- /dev/null +++ b/samples/snippets/decrypt_asymmetric.py @@ -0,0 +1,46 @@ +# Copyright 2020 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 + + +# [START kms_decrypt_asymmetric] +def decrypt_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, ciphertext): + """ + Decrypt the ciphertext using an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to use (e.g. '1'). + ciphertext (bytes): Encrypted bytes to decrypt. + + Returns: + DecryptResponse: Response including plaintext. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + decrypt_response = client.asymmetric_decrypt(key_version_name, ciphertext) + print('Plaintext: {}'.format(decrypt_response.plaintext)) + return decrypt_response +# [END kms_decrypt_asymmetric] diff --git a/samples/snippets/decrypt_symmetric.py b/samples/snippets/decrypt_symmetric.py new file mode 100644 index 00000000..a5cbe714 --- /dev/null +++ b/samples/snippets/decrypt_symmetric.py @@ -0,0 +1,45 @@ +# Copyright 2020 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 + + +# [START kms_decrypt_symmetric] +def decrypt_symmetric(project_id, location_id, key_ring_id, key_id, ciphertext): + """ + Decrypt the ciphertext using the symmetric key + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + ciphertext (bytes): Encrypted bytes to decrypt. + + Returns: + DecryptResponse: Response including plaintext. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + decrypt_response = client.decrypt(key_name, ciphertext) + print('Plaintext: {}'.format(decrypt_response.plaintext)) + return decrypt_response +# [END kms_decrypt_symmetric] diff --git a/samples/snippets/destroy_key_version.py b/samples/snippets/destroy_key_version.py new file mode 100644 index 00000000..7423ca7e --- /dev/null +++ b/samples/snippets/destroy_key_version.py @@ -0,0 +1,45 @@ +# Copyright 2020 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 + + +# [START kms_destroy_key_version] +def destroy_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Schedule destruction of the given key version. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to destroy (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + destroyed_version = client.destroy_crypto_key_version(key_version_name) + print('Destroyed key version: {}'.format(destroyed_version.name)) + return destroyed_version +# [END kms_destroy_key_version] diff --git a/samples/snippets/disable_key_version.py b/samples/snippets/disable_key_version.py new file mode 100644 index 00000000..a4a16dd5 --- /dev/null +++ b/samples/snippets/disable_key_version.py @@ -0,0 +1,55 @@ +# Copyright 2020 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 + + +# [START kms_disable_key_version] +def disable_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Disable a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to disable (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Build the key version. We need to build a full proto instead of a dict due + # to https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key_version = resources_pb2.CryptoKeyVersion() + key_version.name = key_version_name + key_version.state = kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + # Build the update mask. + update_mask = {'paths': ['state']} + + # Call the API. + disabled_version = client.update_crypto_key_version(key_version, update_mask) + print('Disabled key version: {}'.format(disabled_version.name)) + return disabled_version +# [END kms_disable_key_version] diff --git a/samples/snippets/enable_key_version.py b/samples/snippets/enable_key_version.py new file mode 100644 index 00000000..9cb8daad --- /dev/null +++ b/samples/snippets/enable_key_version.py @@ -0,0 +1,55 @@ +# Copyright 2020 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 + + +# [START kms_enable_key_version] +def enable_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Enable a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to enable (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Build the key version. We need to build a full proto instead of a dict due + # to https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key_version = resources_pb2.CryptoKeyVersion() + key_version.name = key_version_name + key_version.state = kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED + + # Build the update mask. + update_mask = {'paths': ['state']} + + # Call the API. + enabled_version = client.update_crypto_key_version(key_version, update_mask) + print('Enabled key version: {}'.format(enabled_version.name)) + return enabled_version +# [END kms_enable_key_version] diff --git a/samples/snippets/encrypt_asymmetric.py b/samples/snippets/encrypt_asymmetric.py new file mode 100644 index 00000000..efe40322 --- /dev/null +++ b/samples/snippets/encrypt_asymmetric.py @@ -0,0 +1,69 @@ +# Copyright 2020 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 + + +# [START kms_encrypt_asymmetric] +def encrypt_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, plaintext): + """ + Encrypt plaintext using the public key portion of an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to use (e.g. '1'). + plaintext (string): message to encrypt + + Returns: + bytes: Encrypted ciphertext. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Import cryptographic helpers from the cryptography package. + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding + + # Convert the plaintext to bytes. + plaintext_bytes = plaintext.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + + # Construct the padding. Note that the padding differs based on key choice. + sha256 = hashes.SHA256() + mgf = padding.MGF1(algorithm=sha256) + pad = padding.OAEP(mgf=mgf, algorithm=sha256, label=None) + + # Encrypt the data using the public key. + ciphertext = rsa_key.encrypt(plaintext_bytes, pad) + print('Ciphertext: {}'.format(base64.b64encode(ciphertext))) + return ciphertext +# [END kms_encrypt_asymmetric] diff --git a/samples/snippets/encrypt_symmetric.py b/samples/snippets/encrypt_symmetric.py new file mode 100644 index 00000000..b90da358 --- /dev/null +++ b/samples/snippets/encrypt_symmetric.py @@ -0,0 +1,51 @@ +# Copyright 2020 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 + + +# [START kms_encrypt_symmetric] +def encrypt_symmetric(project_id, location_id, key_ring_id, key_id, plaintext): + """ + Encrypt plaintext using a symmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + plaintext (string): message to encrypt + + Returns: + bytes: Encrypted ciphertext. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Convert the plaintext to bytes. + plaintext_bytes = plaintext.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + encrypt_response = client.encrypt(key_name, plaintext_bytes) + print('Ciphertext: {}'.format(base64.b64encode(encrypt_response.ciphertext))) + return encrypt_response +# [END kms_encrypt_symmetric] diff --git a/samples/snippets/get_key_labels.py b/samples/snippets/get_key_labels.py new file mode 100644 index 00000000..363bcfba --- /dev/null +++ b/samples/snippets/get_key_labels.py @@ -0,0 +1,48 @@ +# Copyright 2020 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 + + +# [START kms_get_key_labels] +def get_key_labels(project_id, location_id, key_ring_id, key_id): + """ + Get a key and its labels. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + key = client.get_crypto_key(key_name) + + # Example of iterating over labels. + for k, v in key.labels.items(): + print('{} = {}'.format(k, v)) + + return key +# [END kms_get_key_labels] diff --git a/samples/snippets/get_key_version_attestation.py b/samples/snippets/get_key_version_attestation.py new file mode 100644 index 00000000..615d4653 --- /dev/null +++ b/samples/snippets/get_key_version_attestation.py @@ -0,0 +1,56 @@ +# Copyright 2020 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 + + +# [START kms_get_key_version_attestation] +def get_key_version_attestation(project_id, location_id, key_ring_id, key_id, version_id): + """ + Get an HSM-backend key's attestation. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + + Returns: + Attestation: Cloud KMS key attestation. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the attestation. + import base64 + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + version = client.get_crypto_key_version(key_version_name) + + # Only HSM keys have an attestation. For other key types, the attestion + # will be None. + attestation = version.attestation + if not attestation: + raise 'no attestation - attestations only exist on HSM keys' + + encoded_attestation = base64.b64encode(attestation.content) + print('Got key attestation: {}'.format(encoded_attestation)) + return attestation +# [END kms_get_key_version_attestation] diff --git a/samples/snippets/get_public_key.py b/samples/snippets/get_public_key.py new file mode 100644 index 00000000..1b810d15 --- /dev/null +++ b/samples/snippets/get_public_key.py @@ -0,0 +1,45 @@ +# Copyright 2020 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 + + +# [START kms_get_public_key] +def get_public_key(project_id, location_id, key_ring_id, key_id, version_id): + """ + Get the public key for an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key to use (e.g. '1'). + + Returns: + PublicKey: Cloud KMS public key response. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + public_key = client.get_public_key(key_version_name) + print('Public key: {}'.format(public_key.pem)) + return public_key +# [END kms_get_public_key] diff --git a/samples/snippets/iam_add_member.py b/samples/snippets/iam_add_member.py new file mode 100644 index 00000000..442f2483 --- /dev/null +++ b/samples/snippets/iam_add_member.py @@ -0,0 +1,56 @@ +# Copyright 2020 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 + + +# [START kms_iam_add_member] +def iam_add_member(project_id, location_id, key_ring_id, key_id, member): + """ + Add an IAM member to a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + member (string): Member to add (e.g. 'user:foo@example.com') + + Returns: + Policy: Updated Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Add the member to the policy. + policy.bindings.add( + role='roles/cloudkms.cryptoKeyEncrypterDecrypter', + members=[member]) + + # Save the updated IAM policy. + updated_policy = client.set_iam_policy(resource_name, policy) + print('Added {} to {}'.format(member, resource_name)) + return updated_policy +# [END kms_iam_add_member] diff --git a/samples/snippets/iam_get_policy.py b/samples/snippets/iam_get_policy.py new file mode 100644 index 00000000..c00172e9 --- /dev/null +++ b/samples/snippets/iam_get_policy.py @@ -0,0 +1,54 @@ +# Copyright 2020 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 + + +# [START kms_iam_get_policy] +def iam_get_policy(project_id, location_id, key_ring_id, key_id): + """ + Get the IAM policy for a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + Policy: Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Print the policy + print('IAM policy for {}'.format(resource_name)) + for binding in policy.bindings: + print(binding.role) + for member in binding.members: + print('- {}'.format(member)) + + return policy +# [END kms_iam_get_policy] diff --git a/samples/snippets/iam_remove_member.py b/samples/snippets/iam_remove_member.py new file mode 100644 index 00000000..ad73fab9 --- /dev/null +++ b/samples/snippets/iam_remove_member.py @@ -0,0 +1,57 @@ +# Copyright 2020 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 + + +# [START kms_iam_remove_member] +def iam_remove_member(project_id, location_id, key_ring_id, key_id, member): + """ + Remove an IAM member from a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + member (string): Member to remove (e.g. 'user:foo@example.com') + + Returns: + Policy: Updated Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Remove the member from the policy. + for binding in policy.bindings: + if binding.role == 'roles/cloudkms.cryptoKeyEncrypterDecrypter': + if member in binding.members: + binding.members.remove(member) + + # Save the updated IAM policy. + updated_policy = client.set_iam_policy(resource_name, policy) + print('Removed {} from {}'.format(member, resource_name)) + return updated_policy +# [END kms_iam_remove_member] diff --git a/samples/snippets/quickstart.py b/samples/snippets/quickstart.py index 2f97f38f..91b5a49a 100644 --- a/samples/snippets/quickstart.py +++ b/samples/snippets/quickstart.py @@ -13,41 +13,37 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -import os +import argparse -def run_quickstart(): - # [START kms_quickstart] - # Imports the Google APIs client library - from google.cloud import kms_v1 +# [START kms_quickstart] +def quickstart(project_id, location_id): + # Import the client library. + from google.cloud import kms - # Your Google Cloud Platform project ID - project_id = 'YOUR_PROJECT_ID' - # [END kms_quickstart] - project_id = os.environ['GCLOUD_PROJECT'] - # [START kms_quickstart] + # Create the client. + client = kms.KeyManagementServiceClient() - # Lists keys in the "global" location. - location = 'global' + # Build the parent location name. + location_name = client.location_path(project_id, location_id) - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() + # Call the API. + key_rings = client.list_key_rings(location_name) - # The resource name of the location associated with the key rings. - parent = client.location_path(project_id, location) + # Example of iterating over key rings. + for key_ring in key_rings: + print(key_ring.name) - # Lists key rings - response = client.list_key_rings(parent) - response_list = list(response) - - if len(response_list) > 0: - print('Key rings:') - for key_ring in response_list: - print(key_ring.name) - else: - print('No key rings found.') - # [END kms_quickstart] + return key_rings +# [END kms_quickstart] if __name__ == '__main__': - run_quickstart() + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument('project_id', help='id of the GCP project') + parser.add_argument('location_id', help='id of the KMS location') + args = parser.parse_args() + + quickstart(args.project_id, args.location_id) diff --git a/samples/snippets/quickstart_test.py b/samples/snippets/quickstart_test.py deleted file mode 100644 index 5a457518..00000000 --- a/samples/snippets/quickstart_test.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2017 Google Inc. 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. - - -def test_quickstart(): - import quickstart - - quickstart.run_quickstart() diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 8855f3cf..d3e30fa6 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,2 +1 @@ -backoff==1.10.0 -pytest==5.3.2 +pytest==5.4.1 diff --git a/samples/snippets/restore_key_version.py b/samples/snippets/restore_key_version.py new file mode 100644 index 00000000..3c4668d6 --- /dev/null +++ b/samples/snippets/restore_key_version.py @@ -0,0 +1,45 @@ +# Copyright 2020 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 + + +# [START kms_restore_key_version] +def restore_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Restore a key version scheduled for destruction. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + + Returns: + CryptoKeyVersion: Restored Cloud KMS key version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + restored_version = client.restore_crypto_key_version(key_version_name) + print('Restored key version: {}'.format(restored_version.name)) + return restored_version +# [END kms_restore_key_version] diff --git a/samples/snippets/sign_asymmetric.py b/samples/snippets/sign_asymmetric.py new file mode 100644 index 00000000..a92a13ec --- /dev/null +++ b/samples/snippets/sign_asymmetric.py @@ -0,0 +1,64 @@ +# Copyright 2020 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 + + +# [START kms_sign_asymmetric] +def sign_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, message): + """ + Sign a message using the public key part of an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): Version to use (e.g. '1'). + message (string): Message to sign. + + Returns: + AsymmetricSignResponse: Signature. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Import hashlib for calculating hashes. + import hashlib + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Calculate the hash. + hash_ = hashlib.sha256(message_bytes).digest() + + # Build the digest. + # + # Note: Key algorithms will require a varying hash function. For + # example, EC_SIGN_P384_SHA384 requires SHA-384. + digest = {'sha256': hash_} + + # Call the API + sign_response = client.asymmetric_sign(key_version_name, digest) + print('Signature: {}'.format(base64.b64encode(sign_response.signature))) + return sign_response +# [END kms_sign_asymmetric] diff --git a/samples/snippets/snippets.py b/samples/snippets/snippets.py deleted file mode 100644 index e09a3a79..00000000 --- a/samples/snippets/snippets.py +++ /dev/null @@ -1,375 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2017 Google, Inc -# -# 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 - - -# [START kms_create_keyring] -def create_key_ring(project_id, location_id, key_ring_id): - """Creates a KeyRing in the given location (e.g. global).""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the location associated with the KeyRing. - parent = client.location_path(project_id, location_id) - - # The keyring object template - keyring_name = client.key_ring_path(project_id, location_id, key_ring_id) - keyring = {'name': keyring_name} - - # Create a KeyRing - response = client.create_key_ring(parent, key_ring_id, keyring) - - print('Created KeyRing {}.'.format(response.name)) - return response -# [END kms_create_keyring] - - -# [START kms_create_cryptokey] -def create_crypto_key(project_id, location_id, key_ring_id, crypto_key_id): - """Creates a CryptoKey within a KeyRing in the given location.""" - - from google.cloud import kms_v1 - from google.cloud.kms_v1 import enums - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing associated with the CryptoKey. - parent = client.key_ring_path(project_id, location_id, key_ring_id) - - # Create the CryptoKey object template - purpose = enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT - crypto_key = {'purpose': purpose} - - # Create a CryptoKey for the given KeyRing. - response = client.create_crypto_key(parent, crypto_key_id, crypto_key) - - print('Created CryptoKey {}.'.format(response.name)) - return response -# [END kms_create_cryptokey] - - -# [START kms_encrypt] -def encrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, - plaintext): - """Encrypts input plaintext data using the provided symmetric CryptoKey.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - name = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - - # Use the KMS API to encrypt the data. - response = client.encrypt(name, plaintext) - return response.ciphertext -# [END kms_encrypt] - - -# [START kms_decrypt] -def decrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, - ciphertext): - """Decrypts input ciphertext using the provided symmetric CryptoKey.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - name = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - # Use the KMS API to decrypt the data. - response = client.decrypt(name, ciphertext) - return response.plaintext -# [END kms_decrypt] - - -# [START kms_disable_cryptokey_version] -def disable_crypto_key_version(project_id, location_id, key_ring_id, - crypto_key_id, version_id): - """Disables a CryptoKeyVersion associated with a given CryptoKey and - KeyRing.""" - - from google.cloud import kms_v1 - from google.cloud.kms_v1 import enums - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to disable the CryptoKeyVersion. - new_state = enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED - version = {'name': name, 'state': new_state} - update_mask = {'paths': ["state"]} - - # Print results - response = client.update_crypto_key_version(version, update_mask) - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) -# [END kms_disable_cryptokey_version] - - -# [START kms_enable_cryptokey_version] -def enable_crypto_key_version(project_id, location_id, key_ring_id, - crypto_key_id, version_id): - """Enables a CryptoKeyVersion associated with a given CryptoKey and - KeyRing.""" - - from google.cloud import kms_v1 - from google.cloud.kms_v1 import enums - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to enable the CryptoKeyVersion. - new_state = enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED - version = {'name': name, 'state': new_state} - update_mask = {'paths': ["state"]} - - # Print results - response = client.update_crypto_key_version(version, update_mask) - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) -# [END kms_enable_cryptokey_version] - - -# [START kms_destroy_cryptokey_version] -def destroy_crypto_key_version( - project_id, location_id, key_ring_id, crypto_key_id, version_id): - """Schedules a CryptoKeyVersion associated with a given CryptoKey and - KeyRing for destruction 24 hours in the future.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to mark the CryptoKeyVersion for destruction. - response = client.destroy_crypto_key_version(name) - - # Print results - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) -# [END kms_destroy_cryptokey_version] - - -# [START kms_restore_cryptokey_version] -def restore_crypto_key_version( - project_id, location_id, key_ring_id, crypto_key_id, version_id): - """Restores a CryptoKeyVersion that is scheduled for destruction.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to restore the CryptoKeyVersion. - response = client.restore_crypto_key_version(name) - - # Print results - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) - - -# [END kms_restore_cryptokey_version] - - -# [START kms_add_member_to_cryptokey_policy] -def add_member_to_crypto_key_policy( - project_id, location_id, key_ring_id, crypto_key_id, member, role): - """Adds a member with a given role to the Identity and Access Management - (IAM) policy for a given CryptoKey associated with a KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Add member - policy.bindings.add( - role=role, - members=[member]) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} added with role {} to policy for CryptoKey {} \ - in KeyRing {}'.format(member, role, crypto_key_id, key_ring_id)) -# [END kms_add_member_to_cryptokey_policy] - - -# [START kms_add_member_to_keyring_policy] -def add_member_to_key_ring_policy( - project_id, location_id, key_ring_id, member, role): - """Adds a member with a given role to the Identity and Access Management - (IAM) policy for a given KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing. - resource = client.key_ring_path(project_id, location_id, key_ring_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Add member - policy.bindings.add( - role=role, - members=[member]) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} added with role {} to policy in KeyRing {}' - .format(member, role, key_ring_id)) - -# [END kms_add_member_to_keyring_policy] - - -# [START kms_remove_member_from_cryptokey_policy] -def remove_member_from_crypto_key_policy( - project_id, location_id, key_ring_id, crypto_key_id, member, role): - """Removes a member with a given role from the Identity and Access - Management (IAM) policy for a given CryptoKey associated with a KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Remove member - for b in list(policy.bindings): - if b.role == role and member in b.members: - b.members.remove(member) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} removed from role {} for CryptoKey in KeyRing {}' - .format(member, role, crypto_key_id, key_ring_id)) -# [END kms_remove_member_from_cryptokey_policy] - - -def remove_member_from_key_ring_policy(project_id, location_id, key_ring_id, - member, role): - """Removes a member with a given role from the Identity and Access - Management (IAM) policy for a given KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing. - resource = client.key_ring_path(project_id, location_id, key_ring_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Remove member - for b in list(policy.bindings): - if b.role == role and member in b.members: - b.members.remove(member) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} removed from role {} for KeyRing {}' - .format(member, role, key_ring_id)) - - -# [START kms_get_keyring_policy] -def get_key_ring_policy(project_id, location_id, key_ring_id): - """Gets the Identity and Access Management (IAM) policy for a given KeyRing - and prints out roles and the members assigned to those roles.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing. - resource = client.key_ring_path(project_id, location_id, key_ring_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Print results - print('Printing IAM policy for resource {}:'.format(resource)) - for b in policy.bindings: - for m in b.members: - print('Role: {} Member: {}'.format(b.role, m)) - return policy -# [END kms_get_keyring_policy] - - -def get_crypto_key_policy(project_id, location_id, key_ring_id, crypto_key_id): - """Gets the Identity and Access Management (IAM) policy for a given KeyRing - and prints out roles and the members assigned to those roles.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Print results - print('Printing IAM policy for resource {}:'.format(resource)) - for b in policy.bindings: - for m in b.members: - print('Role: {} Member: {}'.format(b.role, m)) - return policy diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index 0b03bcda..c75b872c 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright 2017 Google, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,226 +11,415 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and +import hashlib +import os import time -from os import environ import uuid -from google.api_core.exceptions import Aborted, GoogleAPICallError -from google.cloud import kms_v1 -from google.cloud.kms_v1 import enums -from google.iam.v1.policy_pb2 import Policy -import backoff import pytest -import snippets +from create_key_asymmetric_decrypt import create_key_asymmetric_decrypt +from create_key_asymmetric_sign import create_key_asymmetric_sign +from create_key_hsm import create_key_hsm +from create_key_labels import create_key_labels +from create_key_ring import create_key_ring +from create_key_rotation_schedule import create_key_rotation_schedule +from create_key_symmetric_encrypt_decrypt import create_key_symmetric_encrypt_decrypt +from create_key_version import create_key_version +from decrypt_asymmetric import decrypt_asymmetric +from decrypt_symmetric import decrypt_symmetric +from destroy_key_version import destroy_key_version +from disable_key_version import disable_key_version +from enable_key_version import enable_key_version +from encrypt_asymmetric import encrypt_asymmetric +from encrypt_symmetric import encrypt_symmetric +from get_key_labels import get_key_labels +from get_key_version_attestation import get_key_version_attestation +from get_public_key import get_public_key +from iam_add_member import iam_add_member +from iam_get_policy import iam_get_policy +from iam_remove_member import iam_remove_member +from quickstart import quickstart +from restore_key_version import restore_key_version +from sign_asymmetric import sign_asymmetric +from update_key_add_rotation import update_key_add_rotation +from update_key_remove_labels import update_key_remove_labels +from update_key_remove_rotation import update_key_remove_rotation +from update_key_set_primary import update_key_set_primary +from update_key_update_labels import update_key_update_labels +from verify_asymmetric_ec import verify_asymmetric_ec +from verify_asymmetric_rsa import verify_asymmetric_rsa +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, utils -def create_key_helper(key_id, purpose, algorithm, t): - try: - client = kms_v1.KeyManagementServiceClient() - parent = client.key_ring_path(t.project_id, t.location, t.keyring_id) - - crypto_key = {'purpose': purpose, - 'version_template': {'algorithm': algorithm}} - client.create_crypto_key(parent, key_id, crypto_key) - return True - except GoogleAPICallError: - # key already exists - return False - - -def setup_module(module): - """ - Set up keys in project if needed - """ - t = TestKMSSnippets() - try: - # create keyring - snippets.create_key_ring(t.project_id, t.location, t.keyring_id) - except GoogleAPICallError: - # keyring already exists - pass - s = create_key_helper(t.sym_id, - enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - GOOGLE_SYMMETRIC_ENCRYPTION, - t) - if s: - # leave time for key to initialize - time.sleep(20) - - -class TestKMSSnippets: - project_id = environ['GCLOUD_PROJECT'] - keyring_id = 'kms-samples-{}'.format(uuid.uuid4().hex) - location = 'global' - parent = 'projects/{}/locations/{}'.format(project_id, location) - keyring_path = '{}/keyRings/{}'.format(parent, keyring_id) - version = '1' - - sym_id = 'symmetric' - - sym = '{}/cryptoKeys/{}'.format(keyring_path, sym_id) - sym_version = '{}/cryptoKeyVersions/{}'.format(sym, version) - - message = 'test message 123' - message_bytes = message.encode('utf-8') +from google.cloud import kms +from google.cloud.kms_v1.proto import resources_pb2 + + +@pytest.fixture(scope="module") +def client(): + return kms.KeyManagementServiceClient() + + +@pytest.fixture(scope="module") +def project_id(): + return os.environ['GCLOUD_PROJECT'] + + +@pytest.fixture(scope="module") +def location_id(): + return "us-east1" + + +@pytest.fixture(scope="module") +def key_ring_id(client, project_id, location_id): + location_name = client.location_path(project_id, location_id) + key_ring_id = '{}'.format(uuid.uuid4()) + key_ring = client.create_key_ring(location_name, key_ring_id, {}) + + yield key_ring_id + + for key in client.list_crypto_keys(key_ring.name): + if key.rotation_period.seconds > 0 or key.next_rotation_time.seconds > 0: + # https://github.com/googleapis/gapic-generator-python/issues/364 + updated_key = resources_pb2.CryptoKey() + updated_key.name = key.name + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + client.update_crypto_key(updated_key, update_mask) + + f = 'state != DESTROYED AND state != DESTROY_SCHEDULED' + for version in client.list_crypto_key_versions(key.name, filter_=f): + client.destroy_crypto_key_version(version.name) + + +@pytest.fixture(scope="module") +def asymmetric_decrypt_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def asymmetric_sign_ec_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.EC_SIGN_P256_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def asymmetric_sign_rsa_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def hsm_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION, + 'protection_level': kms.enums.ProtectionLevel.HSM + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def symmetric_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +def wait_for_ready(client, key_version_name): + for i in range(5): + key_version = client.get_crypto_key_version(key_version_name) + if key_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED: + return + time.sleep(0.1*(i**2)) + pytest.fail('{} not ready'.format(key_version_name)) + + +def test_create_key_asymmetric_decrypt(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + + +def test_create_key_asymmetric_sign(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_asymmetric_sign(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + + +def test_create_key_hsm(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_hsm(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + assert key.version_template.protection_level == kms.enums.ProtectionLevel.HSM + + +def test_create_key_labels(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_labels(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + assert key.labels == {'team': 'alpha', 'cost_center': 'cc1234'} + + +def test_create_key_ring(project_id, location_id): + key_ring_id = '{}'.format(uuid.uuid4()) + key_ring = create_key_ring(project_id, location_id, key_ring_id) + assert key_ring + + +def test_create_key_rotation_schedule(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_rotation_schedule(project_id, location_id, key_ring_id, key_id) + assert key.rotation_period.seconds == 60*60*24*30 + assert key.next_rotation_time.seconds > 0 + + +def test_create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + + +def test_create_key_version(project_id, location_id, key_ring_id, symmetric_key_id): + version = create_key_version(project_id, location_id, key_ring_id, symmetric_key_id) + assert version + + +def test_decrypt_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + message = 'my message'.encode('utf-8') + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + public_key = client.get_public_key(key_version_name) + + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + + pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None) + ciphertext = rsa_key.encrypt(message, pad) + + response = decrypt_asymmetric(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1', ciphertext) + assert response.plaintext == message + + +def test_decrypt_symmetric(client, project_id, location_id, key_ring_id, symmetric_key_id): + plaintext = 'my message'.encode('utf-8') + + key_version_name = client.crypto_key_path(project_id, location_id, key_ring_id, symmetric_key_id) + encrypt_response = client.encrypt(key_version_name, plaintext) + ciphertext = encrypt_response.ciphertext + + decrypt_response = decrypt_symmetric(project_id, location_id, key_ring_id, symmetric_key_id, ciphertext) + assert decrypt_response.plaintext == plaintext + + +def test_destroy_restore_key_version(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id) + version = client.create_crypto_key_version(key_name, {}) + version_id = version.name.split('/')[-1] + wait_for_ready(client, version.name) + + destroyed_version = destroy_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert destroyed_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DESTROY_SCHEDULED + + restored_version = restore_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert restored_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + +def test_disable_enable_key_version(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id) + version = client.create_crypto_key_version(key_name, {}) + version_id = version.name.split('/')[-1] + + wait_for_ready(client, version.name) + + disabled_version = disable_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert disabled_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + enabled_version = enable_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert enabled_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED + + +def test_encrypt_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + plaintext = 'my message' + ciphertext = encrypt_asymmetric(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1', plaintext) + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + response = client.asymmetric_decrypt(key_version_name, ciphertext) + assert response.plaintext == plaintext.encode('utf-8') + + +def test_encrypt_symmetric(client, project_id, location_id, key_ring_id, symmetric_key_id): + plaintext = 'my message' + encrypt_response = encrypt_symmetric(project_id, location_id, key_ring_id, symmetric_key_id, plaintext) + + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, symmetric_key_id) + decrypt_response = client.decrypt(key_name, encrypt_response.ciphertext) + assert decrypt_response.plaintext == plaintext.encode('utf-8') + + +def test_get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {'foo': 'bar', 'zip': 'zap'} + + +def test_get_key_version_attestation(project_id, location_id, key_ring_id, hsm_key_id): + attestation = get_key_version_attestation(project_id, location_id, key_ring_id, hsm_key_id, '1') + assert attestation.format + assert attestation.content + + +def test_get_public_key(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + public_key = get_public_key(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + assert public_key.pem + + +def test_iam_add_member(project_id, location_id, key_ring_id, symmetric_key_id): member = 'group:test@google.com' - role = 'roles/viewer' - - @pytest.mark.skip(reason="There's currently no method to delete keyrings, \ - so we should avoid creating resources") - def test_create_key_ring(self): - ring_id = self.keyring_id + '-test-create-{}'.format(uuid.uuid4().hex) - snippets.create_key_ring(self.project_id, self.location, ring_id) - client = kms_v1.KeyManagementServiceClient() - result = client.get_key_ring(client.key_ring_path(self.project_id, - self.location, - ring_id)) - assert ring_id in result.name - - @pytest.mark.skip(reason="Deleting keys isn't instant, so we should avoid \ - creating a large number of them in our tests") - def test_create_crypto_key(self): - key_id = self.sym_id + '-test-{}'.format(uuid.uuid4().hex) - snippets.create_crypto_key(self.project_id, self.location, - self.keyring_id, key_id) - c = kms_v1.KeyManagementServiceClient() - result = c.get_crypto_key(c.crypto_key_path(self.project_id, - self.location, - self.keyring_id, - key_id)) - assert key_id in result.name - - # tests disable/enable/destroy/restore - def test_key_change_version_state(self): - client = kms_v1.KeyManagementServiceClient() - name = client.crypto_key_version_path(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - state_enum = enums.CryptoKeyVersion.CryptoKeyVersionState - # test disable - snippets.disable_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.DISABLED - # test destroy - snippets.destroy_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.DESTROY_SCHEDULED - # test restore - snippets.restore_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.DISABLED - # test re-enable - snippets.enable_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.ENABLED - - def test_get_ring_policy(self): - policy = snippets.get_key_ring_policy(self.project_id, - self.location, self.keyring_id) - assert type(policy) is Policy - - # tests get/add/remove policy members - def test_ring_policy(self): - # add member - snippets.add_member_to_key_ring_policy(self.project_id, self.location, - self.keyring_id, self.member, - self.role) - policy = snippets.get_key_ring_policy(self.project_id, - self.location, self.keyring_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert found - # remove member - snippets.remove_member_from_key_ring_policy(self.project_id, - self.location, - self.keyring_id, - self.member, - self.role) - policy = snippets.get_key_ring_policy(self.project_id, - self.location, self.keyring_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert not found - - # tests get/add/remove policy members - def test_key_policy(self): - # add member - snippets.add_member_to_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id, - self.member, - self.role) - - @backoff.on_exception( - backoff.expo, (Aborted, AssertionError), max_time=60) - def check_policy(): - policy = snippets.get_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert found - - check_policy() - - # remove member - snippets.remove_member_from_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id, - self.member, - self.role) - - @backoff.on_exception( - backoff.expo, (Aborted, AssertionError), max_time=60) - def check_policy_again(): - policy = snippets.get_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert not found - - check_policy_again() - - def test_symmetric_encrypt_decrypt(self): - cipher_bytes = snippets.encrypt_symmetric(self.project_id, - self.location, - self.keyring_id, - self.sym_id, - self.message_bytes) - plain_bytes = snippets.decrypt_symmetric(self.project_id, - self.location, - self.keyring_id, - self.sym_id, - cipher_bytes) - assert plain_bytes == self.message_bytes - assert cipher_bytes != self.message_bytes - plaintext = plain_bytes.decode("utf-8") - assert plaintext == self.message + policy = iam_add_member(project_id, location_id, key_ring_id, symmetric_key_id, member) + assert any(member in b.members for b in policy.bindings) + + +def test_iam_get_policy(project_id, location_id, key_ring_id, symmetric_key_id): + policy = iam_get_policy(project_id, location_id, key_ring_id, symmetric_key_id) + assert policy + + +def test_iam_remove_member(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id) + + policy = client.get_iam_policy(resource_name) + policy.bindings.add( + role='roles/cloudkms.cryptoKeyEncrypterDecrypter', + members=['group:test@google.com', 'group:tester@google.com']) + client.set_iam_policy(resource_name, policy) + + policy = iam_remove_member(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, 'group:test@google.com') + assert not any('group:test@google.com' in b.members for b in policy.bindings) + assert any('group:tester@google.com' in b.members for b in policy.bindings) + + +def test_sign_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + message = 'my message' + + sign_response = sign_asymmetric(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1', message) + assert sign_response.signature + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1') + public_key = client.get_public_key(key_version_name) + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + + try: + sha256 = hashes.SHA256() + pad = padding.PKCS1v15() + rsa_key.verify(sign_response.signature, hash_, pad, utils.Prehashed(sha256)) + except InvalidSignature: + pytest.fail('invalid signature') + + +def test_update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.rotation_period.seconds == 60*60*24*30 + assert key.next_rotation_time.seconds > 0 + + +def test_update_key_remove_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_remove_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {} + + +def test_update_key_remove_rotation(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_remove_rotation(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.rotation_period.seconds == 0 + assert key.next_rotation_time.seconds == 0 + + +def test_update_key_set_primary(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_set_primary(project_id, location_id, key_ring_id, symmetric_key_id, '1') + assert '1' in key.primary.name + + +def test_update_key_update_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_update_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {'new_label': 'new_value'} + + +def test_verify_asymmetric_ec(client, project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id): + message = 'my message' + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id, '1') + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + sign_response = client.asymmetric_sign(key_version_name, {'sha256': hash_}) + + verified = verify_asymmetric_ec(project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id, '1', message, sign_response.signature) + assert verified + + +def test_verify_asymmetric_rsa(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + message = 'my message' + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1') + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + sign_response = client.asymmetric_sign(key_version_name, {'sha256': hash_}) + + verified = verify_asymmetric_rsa(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1', message, sign_response.signature) + assert verified + + +def test_quickstart(project_id, location_id): + key_rings = quickstart(project_id, location_id) + assert key_rings diff --git a/samples/snippets/update_key_add_rotation.py b/samples/snippets/update_key_add_rotation.py new file mode 100644 index 00000000..22dd6b66 --- /dev/null +++ b/samples/snippets/update_key_add_rotation.py @@ -0,0 +1,62 @@ +# Copyright 2020 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 + + +# [START kms_update_key_add_rotation_schedule] +def update_key_add_rotation(project_id, location_id, key_ring_id, key_id): + """ + Add a rotation schedule to an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Import time for getting the current time. + import time + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + + # Rotate the key every 30 days. + key.rotation_period.seconds = 60*60*24*30 + + # Start the first rotation in 24 hours. + key.next_rotation_time.seconds = int(time.time()) + 60*60*24 + + # Build the update mask. + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_add_rotation_schedule] diff --git a/samples/snippets/update_key_remove_labels.py b/samples/snippets/update_key_remove_labels.py new file mode 100644 index 00000000..a44ab214 --- /dev/null +++ b/samples/snippets/update_key_remove_labels.py @@ -0,0 +1,54 @@ +# Copyright 2020 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 + + +# [START kms_update_key_remove_labels] +def update_key_remove_labels(project_id, location_id, key_ring_id, key_id): + """ + Remove labels from an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + key.labels.clear() + + # Build the update mask. + update_mask = {'paths': ['labels']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_remove_labels] diff --git a/samples/snippets/update_key_remove_rotation.py b/samples/snippets/update_key_remove_rotation.py new file mode 100644 index 00000000..7f8707eb --- /dev/null +++ b/samples/snippets/update_key_remove_rotation.py @@ -0,0 +1,53 @@ +# Copyright 2020 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 + + +# [START kms_update_key_remove_rotation_schedule] +def update_key_remove_rotation(project_id, location_id, key_ring_id, key_id): + """ + Remove a rotation schedule from an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + + # Build the update mask. + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_remove_rotation_schedule] diff --git a/samples/snippets/update_key_set_primary.py b/samples/snippets/update_key_set_primary.py new file mode 100644 index 00000000..dd889dbd --- /dev/null +++ b/samples/snippets/update_key_set_primary.py @@ -0,0 +1,45 @@ +# Copyright 2020 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 + + +# [START kms_update_key_set_primary] +def update_key_set_primary(project_id, location_id, key_ring_id, key_id, version_id): + """ + Update the primary version of a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key to make primary (e.g. '2'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + updated_key = client.update_crypto_key_primary_version(key_name, version_id) + print('Updated {} primary to {}'.format(updated_key.name, version_id)) + return updated_key +# [END kms_update_key_set_primary] diff --git a/samples/snippets/update_key_update_labels.py b/samples/snippets/update_key_update_labels.py new file mode 100644 index 00000000..21372472 --- /dev/null +++ b/samples/snippets/update_key_update_labels.py @@ -0,0 +1,54 @@ +# Copyright 2020 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 + + +# [START kms_update_key_update_labels] +def update_key_update_labels(project_id, location_id, key_ring_id, key_id): + """ + Update labels on an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + key.labels.update({'new_label': 'new_value'}) + + # Build the update mask. + update_mask = {'paths': ['labels']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_update_labels] diff --git a/samples/snippets/verify_asymmetric_ec.py b/samples/snippets/verify_asymmetric_ec.py new file mode 100644 index 00000000..ac77a64b --- /dev/null +++ b/samples/snippets/verify_asymmetric_ec.py @@ -0,0 +1,72 @@ +# Copyright 2020 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 + + +# [START kms_verify_asymmetric_signature_ec] +def verify_asymmetric_ec(project_id, location_id, key_ring_id, key_id, version_id, message, signature): + """ + Verify the signature of an message signed with an asymmetric EC key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + message (string): Original message (e.g. 'my message') + signature (bytes): Signature from a sign request. + + Returns: + bool: True if verified, False otherwise + + """ + + # Import the client library. + from google.cloud import kms + + # Import cryptographic helpers from the cryptography package. + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import ec, utils + + # Import hashlib. + import hashlib + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + ec_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message_bytes).digest() + + # Attempt to verify. + try: + sha256 = hashes.SHA256() + ec_key.verify(signature, hash_, ec.ECDSA(utils.Prehashed(sha256))) + print('Signature verified') + return True + except InvalidSignature: + print('Signature failed to verify') + return False +# [END kms_verify_asymmetric_signature_ec] diff --git a/samples/snippets/verify_asymmetric_rsa.py b/samples/snippets/verify_asymmetric_rsa.py new file mode 100644 index 00000000..6df3d862 --- /dev/null +++ b/samples/snippets/verify_asymmetric_rsa.py @@ -0,0 +1,73 @@ +# Copyright 2020 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 + + +# [START kms_verify_asymmetric_signature_rsa] +def verify_asymmetric_rsa(project_id, location_id, key_ring_id, key_id, version_id, message, signature): + """ + Verify the signature of an message signed with an asymmetric RSA key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + message (string): Original message (e.g. 'my message') + signature (bytes): Signature from a sign request. + + Returns: + bool: True if verified, False otherwise + + """ + + # Import the client library. + from google.cloud import kms + + # Import cryptographic helpers from the cryptography package. + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding, utils + + # Import hashlib. + import hashlib + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message_bytes).digest() + + # Attempt to verify. + try: + sha256 = hashes.SHA256() + pad = padding.PKCS1v15() + rsa_key.verify(signature, hash_, pad, utils.Prehashed(sha256)) + print('Signature verified') + return True + except InvalidSignature: + print('Signature failed to verify') + return False +# [END kms_verify_asymmetric_signature_rsa] From 1b1d5dab0b29e04aef511093306f540aab5c7592 Mon Sep 17 00:00:00 2001 From: Takashi Matsuo Date: Tue, 12 May 2020 17:30:32 -0700 Subject: [PATCH 48/52] chore: some lint fixes [(#3748)](https://github.com/GoogleCloudPlatform/python-docs-samples/issues/3748) --- samples/snippets/snippets_test.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/samples/snippets/snippets_test.py b/samples/snippets/snippets_test.py index c75b872c..795edeb4 100644 --- a/samples/snippets/snippets_test.py +++ b/samples/snippets/snippets_test.py @@ -16,6 +16,12 @@ import time import uuid +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, utils +from google.cloud import kms +from google.cloud.kms_v1.proto import resources_pb2 import pytest from create_key_asymmetric_decrypt import create_key_asymmetric_decrypt @@ -50,14 +56,6 @@ from verify_asymmetric_ec import verify_asymmetric_ec from verify_asymmetric_rsa import verify_asymmetric_rsa -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import padding, utils - -from google.cloud import kms -from google.cloud.kms_v1.proto import resources_pb2 - @pytest.fixture(scope="module") def client(): From 2bc94304d57bf2ff6043ceac8735d7420871979c Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 28 May 2020 01:45:21 +0000 Subject: [PATCH 49/52] chore: update templates --- .flake8 | 2 ++ .gitignore | 2 ++ .kokoro/publish-docs.sh | 2 -- .kokoro/release.sh | 2 -- MANIFEST.in | 3 +++ noxfile.py | 17 +++++++++-------- samples/snippets/README.rst | 20 ++++++++++++++++++-- synth.metadata | 23 +++++++---------------- synth.py | 9 ++++++++- 9 files changed, 49 insertions(+), 31 deletions(-) diff --git a/.flake8 b/.flake8 index 20fe9bda..ed931638 100644 --- a/.flake8 +++ b/.flake8 @@ -21,6 +21,8 @@ exclude = # Exclude generated code. **/proto/** **/gapic/** + **/services/** + **/types/** *_pb2.py # Standard linting exemptions. diff --git a/.gitignore b/.gitignore index 3fb06e09..b87e1ed5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ dist build eggs +.eggs parts bin var @@ -49,6 +50,7 @@ bigquery/docs/generated # Virtual environment env/ coverage.xml +sponge_log.xml # System test environment variables. system_tests/local_test_setup diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 3a5dbb78..0d7db6e9 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#!/bin/bash - set -eo pipefail # Disable buffering, so that the logs stream through. diff --git a/.kokoro/release.sh b/.kokoro/release.sh index e98f62b0..5dae4fdc 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#!/bin/bash - set -eo pipefail # Start the releasetool reporter diff --git a/MANIFEST.in b/MANIFEST.in index 68855abc..e9e29d12 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -20,3 +20,6 @@ recursive-include google *.json *.proto recursive-include tests * global-exclude *.py[co] global-exclude __pycache__ + +# Exclude scripts for samples readmegen +prune scripts/readme-gen \ No newline at end of file diff --git a/noxfile.py b/noxfile.py index 97b8095a..552de1b0 100644 --- a/noxfile.py +++ b/noxfile.py @@ -26,11 +26,12 @@ BLACK_VERSION = "black==19.3b0" BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] -if os.path.exists("samples"): - BLACK_PATHS.append("samples") +DEFAULT_PYTHON_VERSION = "3.7" +SYSTEM_TEST_PYTHON_VERSIONS = ["2.7", "3.7"] +UNIT_TEST_PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8"] -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): """Run linters. @@ -56,7 +57,7 @@ def blacken(session): session.run("black", *BLACK_PATHS) -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint_setup_py(session): """Verify that setup.py is valid (including RST check).""" session.install("docutils", "pygments") @@ -84,13 +85,13 @@ def default(session): ) -@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"]) +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) def unit(session): """Run the unit test suite.""" default(session) -@nox.session(python=["2.7", "3.7"]) +@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" system_test_path = os.path.join("tests", "system.py") @@ -120,7 +121,7 @@ def system(session): session.run("py.test", "--quiet", system_test_folder_path, *session.posargs) -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def cover(session): """Run the final coverage report. @@ -133,7 +134,7 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python="3.7") +@nox.session(python=DEFAULT_PYTHON_VERSION) def docs(session): """Build the docs for this library.""" diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst index 3acb00f5..7a52a081 100644 --- a/samples/snippets/README.rst +++ b/samples/snippets/README.rst @@ -1,3 +1,4 @@ + .. This file is automatically generated. Do not edit this file directly. Google Cloud KMS API Python Samples @@ -14,10 +15,12 @@ This directory contains samples for Google Cloud KMS API. The `Google Cloud KMS .. _Google Cloud KMS API: https://cloud.google.com/kms/docs/ + Setup ------------------------------------------------------------------------------- + Authentication ++++++++++++++ @@ -28,6 +31,9 @@ credentials for applications. .. _Authentication Getting Started Guide: https://cloud.google.com/docs/authentication/getting-started + + + Install Dependencies ++++++++++++++++++++ @@ -42,7 +48,7 @@ Install Dependencies .. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup -#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. +#. Create a virtualenv. Samples are compatible with Python 3.6+. .. code-block:: bash @@ -58,9 +64,15 @@ Install Dependencies .. _pip: https://pip.pypa.io/ .. _virtualenv: https://virtualenv.pypa.io/ + + + + + Samples ------------------------------------------------------------------------------- + Quickstart +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -79,4 +91,8 @@ To run this sample: -.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file + + + + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ diff --git a/synth.metadata b/synth.metadata index 0289fcb4..29286d25 100644 --- a/synth.metadata +++ b/synth.metadata @@ -1,26 +1,18 @@ { - "updateTime": "2020-04-14T21:47:51.484475Z", "sources": [ { - "generator": { - "name": "artman", - "version": "2.0.0", - "dockerImage": "googleapis/artman@sha256:b3b47805231a305d0f40c4bf069df20f6a2635574e6d4259fac651d3f9f6e098" + "git": { + "name": ".", + "remote": "git@github.com:googleapis/python-kms.git", + "sha": "309319a29b3e0f851d93c58de06b60f50cf0fbfb" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "fea22b1d9f27f86ef355c1d0dba00e0791a08a19", - "internalRef": "306508794" - } - }, - { - "git": { - "name": "synthtool", - "remote": "https://github.com/googleapis/synthtool.git", - "sha": "52638600f387deb98efb5f9c85fec39e82aa9052" + "sha": "eafa840ceec23b44a5c21670288107c661252711", + "internalRef": "313488995" } } ], @@ -31,8 +23,7 @@ "apiName": "kms", "apiVersion": "v1", "language": "python", - "generator": "gapic", - "config": "google/cloud/kms/artman_cloudkms.yaml" + "generator": "bazel" } } ] diff --git a/synth.py b/synth.py index cfdf648e..c17cb683 100644 --- a/synth.py +++ b/synth.py @@ -16,6 +16,7 @@ import synthtool as s import synthtool.gcp as gcp +from synthtool.languages import python import logging logging.basicConfig(level=logging.DEBUG) @@ -71,7 +72,13 @@ def crypto_key_path_path(cls, project, location, key_ring, crypto_key_path): # ---------------------------------------------------------------------------- # Add templated files # ---------------------------------------------------------------------------- -templated_files = common.py_library(cov_level=70) +templated_files = common.py_library(cov_level=70, samples=True) s.move(templated_files) +# ---------------------------------------------------------------------------- +# Samples templates +# ---------------------------------------------------------------------------- + +python.py_samples() + s.shell.run(["nox", "-s", "blacken"], hide_output=False) From 515d022e6291f1077f923b3349ce286e4e89dd56 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 28 May 2020 01:56:36 +0000 Subject: [PATCH 50/52] chore: update templates --- .github/CODEOWNERS | 8 + .kokoro/samples/lint/common.cfg | 34 +++ .kokoro/samples/lint/continuous.cfg | 6 + .kokoro/samples/lint/periodic.cfg | 6 + .kokoro/samples/lint/presubmit.cfg | 6 + .kokoro/samples/python3.6/common.cfg | 34 +++ .kokoro/samples/python3.6/continuous.cfg | 7 + .kokoro/samples/python3.6/periodic.cfg | 6 + .kokoro/samples/python3.6/presubmit.cfg | 6 + .kokoro/samples/python3.7/common.cfg | 34 +++ .kokoro/samples/python3.7/continuous.cfg | 6 + .kokoro/samples/python3.7/periodic.cfg | 6 + .kokoro/samples/python3.7/presubmit.cfg | 6 + .kokoro/samples/python3.8/common.cfg | 34 +++ .kokoro/samples/python3.8/continuous.cfg | 6 + .kokoro/samples/python3.8/periodic.cfg | 6 + .kokoro/samples/python3.8/presubmit.cfg | 6 + .kokoro/test-samples.sh | 104 ++++++++ docs/multiprocessing.rst | 7 + samples/AUTHORING_GUIDE.md | 1 + samples/CONTRIBUTING.md | 1 + samples/snippets/noxfile.py | 225 ++++++++++++++++++ scripts/decrypt-secrets.sh | 33 +++ scripts/readme-gen/readme_gen.py | 66 +++++ scripts/readme-gen/templates/README.tmpl.rst | 87 +++++++ scripts/readme-gen/templates/auth.tmpl.rst | 9 + .../templates/auth_api_key.tmpl.rst | 14 ++ .../templates/install_deps.tmpl.rst | 29 +++ .../templates/install_portaudio.tmpl.rst | 35 +++ testing/.gitignore | 3 + 30 files changed, 831 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .kokoro/samples/lint/common.cfg create mode 100644 .kokoro/samples/lint/continuous.cfg create mode 100644 .kokoro/samples/lint/periodic.cfg create mode 100644 .kokoro/samples/lint/presubmit.cfg create mode 100644 .kokoro/samples/python3.6/common.cfg create mode 100644 .kokoro/samples/python3.6/continuous.cfg create mode 100644 .kokoro/samples/python3.6/periodic.cfg create mode 100644 .kokoro/samples/python3.6/presubmit.cfg create mode 100644 .kokoro/samples/python3.7/common.cfg create mode 100644 .kokoro/samples/python3.7/continuous.cfg create mode 100644 .kokoro/samples/python3.7/periodic.cfg create mode 100644 .kokoro/samples/python3.7/presubmit.cfg create mode 100644 .kokoro/samples/python3.8/common.cfg create mode 100644 .kokoro/samples/python3.8/continuous.cfg create mode 100644 .kokoro/samples/python3.8/periodic.cfg create mode 100644 .kokoro/samples/python3.8/presubmit.cfg create mode 100755 .kokoro/test-samples.sh create mode 100644 docs/multiprocessing.rst create mode 100644 samples/AUTHORING_GUIDE.md create mode 100644 samples/CONTRIBUTING.md create mode 100644 samples/snippets/noxfile.py create mode 100755 scripts/decrypt-secrets.sh create mode 100644 scripts/readme-gen/readme_gen.py create mode 100644 scripts/readme-gen/templates/README.tmpl.rst create mode 100644 scripts/readme-gen/templates/auth.tmpl.rst create mode 100644 scripts/readme-gen/templates/auth_api_key.tmpl.rst create mode 100644 scripts/readme-gen/templates/install_deps.tmpl.rst create mode 100644 scripts/readme-gen/templates/install_portaudio.tmpl.rst create mode 100644 testing/.gitignore diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..d2b4570a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,8 @@ +# Code owners file. +# This file controls who is tagged for review for any given pull request. +# +# For syntax help see: +# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax + + +/samples/ @DanSanche @googleapis/python-samples-owners \ No newline at end of file diff --git a/.kokoro/samples/lint/common.cfg b/.kokoro/samples/lint/common.cfg new file mode 100644 index 00000000..6c5ff5c5 --- /dev/null +++ b/.kokoro/samples/lint/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "lint" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/lint/continuous.cfg b/.kokoro/samples/lint/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/lint/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/lint/periodic.cfg b/.kokoro/samples/lint/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/lint/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/lint/presubmit.cfg b/.kokoro/samples/lint/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/lint/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg new file mode 100644 index 00000000..7de5927f --- /dev/null +++ b/.kokoro/samples/python3.6/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.6" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.6/continuous.cfg b/.kokoro/samples/python3.6/continuous.cfg new file mode 100644 index 00000000..7218af14 --- /dev/null +++ b/.kokoro/samples/python3.6/continuous.cfg @@ -0,0 +1,7 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + diff --git a/.kokoro/samples/python3.6/periodic.cfg b/.kokoro/samples/python3.6/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/python3.6/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.6/presubmit.cfg b/.kokoro/samples/python3.6/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.6/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg new file mode 100644 index 00000000..91390e2a --- /dev/null +++ b/.kokoro/samples/python3.7/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.7" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.7/continuous.cfg b/.kokoro/samples/python3.7/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.7/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/periodic.cfg b/.kokoro/samples/python3.7/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/python3.7/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/presubmit.cfg b/.kokoro/samples/python3.7/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.7/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg new file mode 100644 index 00000000..50e47fd4 --- /dev/null +++ b/.kokoro/samples/python3.8/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.8" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-kms/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-kms/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.8/continuous.cfg b/.kokoro/samples/python3.8/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.8/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/periodic.cfg b/.kokoro/samples/python3.8/periodic.cfg new file mode 100644 index 00000000..50fec964 --- /dev/null +++ b/.kokoro/samples/python3.8/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/presubmit.cfg b/.kokoro/samples/python3.8/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.8/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh new file mode 100755 index 00000000..73bad619 --- /dev/null +++ b/.kokoro/test-samples.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Copyright 2020 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 +# +# https://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. + + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +cd github/python-kms + +# Run periodic samples tests at latest release +if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + LATEST_RELEASE=$(git describe --abbrev=0 --tags) + git checkout $LATEST_RELEASE +fi + +# Disable buffering, so that the logs stream through. +export PYTHONUNBUFFERED=1 + +# Debug: show build environment +env | grep KOKORO + +# Install nox +python3.6 -m pip install --upgrade --quiet nox + +# Use secrets acessor service account to get secrets +if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then + gcloud auth activate-service-account \ + --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \ + --project="cloud-devrel-kokoro-resources" +fi + +# This script will create 3 files: +# - testing/test-env.sh +# - testing/service-account.json +# - testing/client-secrets.json +./scripts/decrypt-secrets.sh + +source ./testing/test-env.sh +export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json + +# For cloud-run session, we activate the service account for gcloud sdk. +gcloud auth activate-service-account \ + --key-file "${GOOGLE_APPLICATION_CREDENTIALS}" + +export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json + +echo -e "\n******************** TESTING PROJECTS ********************" + +# Switch to 'fail at end' to allow all tests to complete before exiting. +set +e +# Use RTN to return a non-zero value if the test fails. +RTN=0 +ROOT=$(pwd) +# Find all requirements.txt in the samples directory (may break on whitespace). +for file in samples/**/requirements.txt; do + cd "$ROOT" + # Navigate to the project folder. + file=$(dirname "$file") + cd "$file" + + echo "------------------------------------------------------------" + echo "- testing $file" + echo "------------------------------------------------------------" + + # Use nox to execute the tests for the project. + python3.6 -m nox -s "$RUN_TESTS_SESSION" + EXIT=$? + + # If this is a periodic build, send the test log to the Build Cop Bot. + # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/buildcop. + if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + chmod +x $KOKORO_GFILE_DIR/linux_amd64/buildcop + $KOKORO_GFILE_DIR/linux_amd64/buildcop + fi + + if [[ $EXIT -ne 0 ]]; then + RTN=1 + echo -e "\n Testing failed: Nox returned a non-zero exit code. \n" + else + echo -e "\n Testing completed.\n" + fi + +done +cd "$ROOT" + +# Workaround for Kokoro permissions issue: delete secrets +rm testing/{test-env.sh,client-secrets.json,service-account.json} + +exit "$RTN" \ No newline at end of file diff --git a/docs/multiprocessing.rst b/docs/multiprocessing.rst new file mode 100644 index 00000000..1cb29d4c --- /dev/null +++ b/docs/multiprocessing.rst @@ -0,0 +1,7 @@ +.. note:: + + Because this client uses :mod:`grpcio` library, it is safe to + share instances across threads. In multiprocessing scenarios, the best + practice is to create client instances *after* the invocation of + :func:`os.fork` by :class:`multiprocessing.Pool` or + :class:`multiprocessing.Process`. diff --git a/samples/AUTHORING_GUIDE.md b/samples/AUTHORING_GUIDE.md new file mode 100644 index 00000000..55c97b32 --- /dev/null +++ b/samples/AUTHORING_GUIDE.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/AUTHORING_GUIDE.md \ No newline at end of file diff --git a/samples/CONTRIBUTING.md b/samples/CONTRIBUTING.md new file mode 100644 index 00000000..34c882b6 --- /dev/null +++ b/samples/CONTRIBUTING.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/CONTRIBUTING.md \ No newline at end of file diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py new file mode 100644 index 00000000..b23055f1 --- /dev/null +++ b/samples/snippets/noxfile.py @@ -0,0 +1,225 @@ +# Copyright 2019 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. + +from __future__ import print_function + +import os +from pathlib import Path +import sys + +import nox + + +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING +# DO NOT EDIT THIS FILE EVER! +# WARNING - WARNING - WARNING - WARNING - WARNING +# WARNING - WARNING - WARNING - WARNING - WARNING + +# Copy `noxfile_config.py` to your directory and modify it instead. + + +# `TEST_CONFIG` dict is a configuration hook that allows users to +# modify the test configurations. The values here should be in sync +# with `noxfile_config.py`. Users will copy `noxfile_config.py` into +# their directory and modify it. + +TEST_CONFIG = { + # You can opt out from the test for specific Python versions. + 'ignored_versions': ["2.7"], + + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + 'gcloud_project_env': 'GCLOUD_PROJECT', + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + 'envs': {}, +} + + +try: + # Ensure we can import noxfile_config in the project's directory. + sys.path.append('.') + from noxfile_config import TEST_CONFIG_OVERRIDE +except ImportError as e: + print("No user noxfile_config found: detail: {}".format(e)) + TEST_CONFIG_OVERRIDE = {} + +# Update the TEST_CONFIG with the user supplied values. +TEST_CONFIG.update(TEST_CONFIG_OVERRIDE) + + +def get_pytest_env_vars(): + """Returns a dict for pytest invocation.""" + ret = {} + + # Override the GCLOUD_PROJECT and the alias. + env_key = TEST_CONFIG['gcloud_project_env'] + # This should error out if not set. + ret['GOOGLE_CLOUD_PROJECT'] = os.environ[env_key] + ret['GCLOUD_PROJECT'] = os.environ[env_key] + + # Apply user supplied envs. + ret.update(TEST_CONFIG['envs']) + return ret + + +# DO NOT EDIT - automatically generated. +# All versions used to tested samples. +ALL_VERSIONS = ["2.7", "3.6", "3.7", "3.8"] + +# Any default versions that should be ignored. +IGNORED_VERSIONS = TEST_CONFIG['ignored_versions'] + +TESTED_VERSIONS = sorted([v for v in ALL_VERSIONS if v not in IGNORED_VERSIONS]) + +INSTALL_LIBRARY_FROM_SOURCE = bool(os.environ.get("INSTALL_LIBRARY_FROM_SOURCE", False)) +# +# Style Checks +# + + +def _determine_local_import_names(start_dir): + """Determines all import names that should be considered "local". + + This is used when running the linter to insure that import order is + properly checked. + """ + file_ext_pairs = [os.path.splitext(path) for path in os.listdir(start_dir)] + return [ + basename + for basename, extension in file_ext_pairs + if extension == ".py" + or os.path.isdir(os.path.join(start_dir, basename)) + and basename not in ("__pycache__") + ] + + +# Linting with flake8. +# +# We ignore the following rules: +# E203: whitespace before ‘:’ +# E266: too many leading ‘#’ for block comment +# E501: line too long +# I202: Additional newline in a section of imports +# +# We also need to specify the rules which are ignored by default: +# ['E226', 'W504', 'E126', 'E123', 'W503', 'E24', 'E704', 'E121'] +FLAKE8_COMMON_ARGS = [ + "--show-source", + "--builtin=gettext", + "--max-complexity=20", + "--import-order-style=google", + "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--max-line-length=88", +] + + +@nox.session +def lint(session): + session.install("flake8", "flake8-import-order") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + "." + ] + session.run("flake8", *args) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests(session, post_install=None): + """Runs py.test for a particular project.""" + if os.path.exists("requirements.txt"): + session.install("-r", "requirements.txt") + + if os.path.exists("requirements-test.txt"): + session.install("-r", "requirements-test.txt") + + if INSTALL_LIBRARY_FROM_SOURCE: + session.install("-e", _get_repo_root()) + + if post_install: + post_install(session) + + session.run( + "pytest", + *(PYTEST_COMMON_ARGS + session.posargs), + # Pytest will return 5 when no tests are collected. This can happen + # on travis where slow and flaky tests are excluded. + # See http://doc.pytest.org/en/latest/_modules/_pytest/main.html + success_codes=[0, 5], + env=get_pytest_env_vars() + ) + + +@nox.session(python=ALL_VERSIONS) +def py(session): + """Runs py.test for a sample using the specified version of Python.""" + if session.python in TESTED_VERSIONS: + _session_tests(session) + else: + session.skip("SKIPPED: {} tests are disabled for this sample.".format( + session.python + )) + + +# +# Readmegen +# + + +def _get_repo_root(): + """ Returns the root folder of the project. """ + # Get root of this repository. Assume we don't have directories nested deeper than 10 items. + p = Path(os.getcwd()) + for i in range(10): + if p is None: + break + if Path(p / ".git").exists(): + return str(p) + p = p.parent + raise Exception("Unable to detect repository root.") + + +GENERATED_READMES = sorted([x for x in Path(".").rglob("*.rst.in")]) + + +@nox.session +@nox.parametrize("path", GENERATED_READMES) +def readmegen(session, path): + """(Re-)generates the readme for a sample.""" + session.install("jinja2", "pyyaml") + dir_ = os.path.dirname(path) + + if os.path.exists(os.path.join(dir_, "requirements.txt")): + session.install("-r", os.path.join(dir_, "requirements.txt")) + + in_file = os.path.join(dir_, "README.rst.in") + session.run( + "python", _get_repo_root() + "/scripts/readme-gen/readme_gen.py", in_file + ) diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh new file mode 100755 index 00000000..ff599eb2 --- /dev/null +++ b/scripts/decrypt-secrets.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. 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. + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT=$( dirname "$DIR" ) + +# Work from the project root. +cd $ROOT + +# Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. +PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" + +gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ + > testing/test-env.sh +gcloud secrets versions access latest \ + --secret="python-docs-samples-service-account" \ + > testing/service-account.json +gcloud secrets versions access latest \ + --secret="python-docs-samples-client-secrets" \ + > testing/client-secrets.json \ No newline at end of file diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py new file mode 100644 index 00000000..d309d6e9 --- /dev/null +++ b/scripts/readme-gen/readme_gen.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# Copyright 2016 Google Inc +# +# 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. + +"""Generates READMEs using configuration defined in yaml.""" + +import argparse +import io +import os +import subprocess + +import jinja2 +import yaml + + +jinja_env = jinja2.Environment( + trim_blocks=True, + loader=jinja2.FileSystemLoader( + os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates')))) + +README_TMPL = jinja_env.get_template('README.tmpl.rst') + + +def get_help(file): + return subprocess.check_output(['python', file, '--help']).decode() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('source') + parser.add_argument('--destination', default='README.rst') + + args = parser.parse_args() + + source = os.path.abspath(args.source) + root = os.path.dirname(source) + destination = os.path.join(root, args.destination) + + jinja_env.globals['get_help'] = get_help + + with io.open(source, 'r') as f: + config = yaml.load(f) + + # This allows get_help to execute in the right directory. + os.chdir(root) + + output = README_TMPL.render(config) + + with io.open(destination, 'w') as f: + f.write(output) + + +if __name__ == '__main__': + main() diff --git a/scripts/readme-gen/templates/README.tmpl.rst b/scripts/readme-gen/templates/README.tmpl.rst new file mode 100644 index 00000000..4fd23976 --- /dev/null +++ b/scripts/readme-gen/templates/README.tmpl.rst @@ -0,0 +1,87 @@ +{# The following line is a lie. BUT! Once jinja2 is done with it, it will + become truth! #} +.. This file is automatically generated. Do not edit this file directly. + +{{product.name}} Python Samples +=============================================================================== + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/README.rst + + +This directory contains samples for {{product.name}}. {{product.description}} + +{{description}} + +.. _{{product.name}}: {{product.url}} + +{% if required_api_url %} +To run the sample, you need to enable the API at: {{required_api_url}} +{% endif %} + +{% if required_role %} +To run the sample, you need to have `{{required_role}}` role. +{% endif %} + +{{other_required_steps}} + +{% if setup %} +Setup +------------------------------------------------------------------------------- + +{% for section in setup %} + +{% include section + '.tmpl.rst' %} + +{% endfor %} +{% endif %} + +{% if samples %} +Samples +------------------------------------------------------------------------------- + +{% for sample in samples %} +{{sample.name}} ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +{% if not sample.hide_cloudshell_button %} +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/{{sample.file}},{{folder}}/README.rst +{% endif %} + + +{{sample.description}} + +To run this sample: + +.. code-block:: bash + + $ python {{sample.file}} +{% if sample.show_help %} + + {{get_help(sample.file)|indent}} +{% endif %} + + +{% endfor %} +{% endif %} + +{% if cloud_client_library %} + +The client library +------------------------------------------------------------------------------- + +This sample uses the `Google Cloud Client Library for Python`_. +You can read the documentation for more details on API usage and use GitHub +to `browse the source`_ and `report issues`_. + +.. _Google Cloud Client Library for Python: + https://googlecloudplatform.github.io/google-cloud-python/ +.. _browse the source: + https://github.com/GoogleCloudPlatform/google-cloud-python +.. _report issues: + https://github.com/GoogleCloudPlatform/google-cloud-python/issues + +{% endif %} + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/scripts/readme-gen/templates/auth.tmpl.rst b/scripts/readme-gen/templates/auth.tmpl.rst new file mode 100644 index 00000000..1446b94a --- /dev/null +++ b/scripts/readme-gen/templates/auth.tmpl.rst @@ -0,0 +1,9 @@ +Authentication +++++++++++++++ + +This sample requires you to have authentication setup. Refer to the +`Authentication Getting Started Guide`_ for instructions on setting up +credentials for applications. + +.. _Authentication Getting Started Guide: + https://cloud.google.com/docs/authentication/getting-started diff --git a/scripts/readme-gen/templates/auth_api_key.tmpl.rst b/scripts/readme-gen/templates/auth_api_key.tmpl.rst new file mode 100644 index 00000000..11957ce2 --- /dev/null +++ b/scripts/readme-gen/templates/auth_api_key.tmpl.rst @@ -0,0 +1,14 @@ +Authentication +++++++++++++++ + +Authentication for this service is done via an `API Key`_. To obtain an API +Key: + +1. Open the `Cloud Platform Console`_ +2. Make sure that billing is enabled for your project. +3. From the **Credentials** page, create a new **API Key** or use an existing + one for your project. + +.. _API Key: + https://developers.google.com/api-client-library/python/guide/aaa_apikeys +.. _Cloud Console: https://console.cloud.google.com/project?_ diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst new file mode 100644 index 00000000..a0406dba --- /dev/null +++ b/scripts/readme-gen/templates/install_deps.tmpl.rst @@ -0,0 +1,29 @@ +Install Dependencies +++++++++++++++++++++ + +#. Clone python-docs-samples and change directory to the sample directory you want to use. + + .. code-block:: bash + + $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git + +#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. + + .. _Python Development Environment Setup Guide: + https://cloud.google.com/python/setup + +#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. + + .. code-block:: bash + + $ virtualenv env + $ source env/bin/activate + +#. Install the dependencies needed to run the samples. + + .. code-block:: bash + + $ pip install -r requirements.txt + +.. _pip: https://pip.pypa.io/ +.. _virtualenv: https://virtualenv.pypa.io/ diff --git a/scripts/readme-gen/templates/install_portaudio.tmpl.rst b/scripts/readme-gen/templates/install_portaudio.tmpl.rst new file mode 100644 index 00000000..5ea33d18 --- /dev/null +++ b/scripts/readme-gen/templates/install_portaudio.tmpl.rst @@ -0,0 +1,35 @@ +Install PortAudio ++++++++++++++++++ + +Install `PortAudio`_. This is required by the `PyAudio`_ library to stream +audio from your computer's microphone. PyAudio depends on PortAudio for cross-platform compatibility, and is installed differently depending on the +platform. + +* For Mac OS X, you can use `Homebrew`_:: + + brew install portaudio + + **Note**: if you encounter an error when running `pip install` that indicates + it can't find `portaudio.h`, try running `pip install` with the following + flags:: + + pip install --global-option='build_ext' \ + --global-option='-I/usr/local/include' \ + --global-option='-L/usr/local/lib' \ + pyaudio + +* For Debian / Ubuntu Linux:: + + apt-get install portaudio19-dev python-all-dev + +* Windows may work without having to install PortAudio explicitly (it will get + installed with PyAudio). + +For more details, see the `PyAudio installation`_ page. + + +.. _PyAudio: https://people.csail.mit.edu/hubert/pyaudio/ +.. _PortAudio: http://www.portaudio.com/ +.. _PyAudio installation: + https://people.csail.mit.edu/hubert/pyaudio/#downloads +.. _Homebrew: http://brew.sh diff --git a/testing/.gitignore b/testing/.gitignore new file mode 100644 index 00000000..b05fbd63 --- /dev/null +++ b/testing/.gitignore @@ -0,0 +1,3 @@ +test-env.sh +service-account.json +client-secrets.json \ No newline at end of file From 990e7a66d00e14c5a94856f2e5ea1f9e3a2e337b Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 28 May 2020 04:07:36 +0000 Subject: [PATCH 51/52] chore: update docs index --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 7ae49b7a..9307e8ff 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,6 @@ .. include:: README.rst +.. include:: multiprocessing.rst API Reference ------------- From 7b36aeb068a6ab39bec89d6ac90b7c7045c1e79c Mon Sep 17 00:00:00 2001 From: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Date: Wed, 3 Jun 2020 16:56:40 -0700 Subject: [PATCH 52/52] chore: only trigger review for python file changes --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d2b4570a..12a98268 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,4 +5,4 @@ # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax -/samples/ @DanSanche @googleapis/python-samples-owners \ No newline at end of file +/samples/**/*.py @DanSanche @googleapis/python-samples-owners