From e15de83d5d0dbd55c0ae503350260ba063482d64 Mon Sep 17 00:00:00 2001 From: Fedor Isakov Date: Thu, 22 Jul 2021 18:22:12 +0300 Subject: [PATCH] feat(samples): private CA python samples --- privateca/snippets/conftest.py | 58 ++++ privateca/snippets/create_ca_pool.py | 53 ++++ privateca/snippets/create_certificate.py | 115 ++++++++ .../snippets/create_certificate_authority.py | 97 +++++++ privateca/snippets/delete_ca_pool.py | 45 +++ .../snippets/delete_certificate_authority.py | 76 ++++++ .../snippets/disable_certificate_authority.py | 58 ++++ .../snippets/enable_certificate_authority.py | 61 +++++ privateca/snippets/list_ca_pools.py | 46 ++++ .../snippets/list_certificate_authorities.py | 42 +++ privateca/snippets/list_certificates.py | 46 ++++ privateca/snippets/noxfile.py | 258 ++++++++++++++++++ privateca/snippets/noxfile_config.py | 38 +++ privateca/snippets/requirements-test.txt | 2 + privateca/snippets/requirements.txt | 2 + privateca/snippets/revoke_certificate.py | 64 +++++ privateca/snippets/test_ca_pools.py | 74 +++++ .../snippets/test_certificate_authorities.py | 96 +++++++ privateca/snippets/test_certificates.py | 115 ++++++++ 19 files changed, 1346 insertions(+) create mode 100644 privateca/snippets/conftest.py create mode 100644 privateca/snippets/create_ca_pool.py create mode 100644 privateca/snippets/create_certificate.py create mode 100644 privateca/snippets/create_certificate_authority.py create mode 100644 privateca/snippets/delete_ca_pool.py create mode 100644 privateca/snippets/delete_certificate_authority.py create mode 100644 privateca/snippets/disable_certificate_authority.py create mode 100644 privateca/snippets/enable_certificate_authority.py create mode 100644 privateca/snippets/list_ca_pools.py create mode 100644 privateca/snippets/list_certificate_authorities.py create mode 100644 privateca/snippets/list_certificates.py create mode 100644 privateca/snippets/noxfile.py create mode 100644 privateca/snippets/noxfile_config.py create mode 100644 privateca/snippets/requirements-test.txt create mode 100644 privateca/snippets/requirements.txt create mode 100644 privateca/snippets/revoke_certificate.py create mode 100644 privateca/snippets/test_ca_pools.py create mode 100644 privateca/snippets/test_certificate_authorities.py create mode 100644 privateca/snippets/test_certificates.py diff --git a/privateca/snippets/conftest.py b/privateca/snippets/conftest.py new file mode 100644 index 000000000000..e3cc338a5039 --- /dev/null +++ b/privateca/snippets/conftest.py @@ -0,0 +1,58 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import uuid + +import google.auth + +import pytest + +from create_ca_pool import create_ca_pool +from create_certificate_authority import create_certificate_authority +from delete_ca_pool import delete_ca_pool +from delete_certificate_authority import delete_certificate_authority + +PROJECT = google.auth.default()[1] +LOCATION = "europe-west1" +COMMON_NAME = "COMMON_NAME" +ORGANIZATION = "ORGANIZATION" +CA_DURATION = 1000000 + + +def generate_name() -> str: + return "test-" + uuid.uuid4().hex[:10] + + +@pytest.fixture +def ca_pool(): + CA_POOL_NAME = generate_name() + + create_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + + yield CA_POOL_NAME + + delete_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + + +@pytest.fixture +def certificate_authority(ca_pool): + CA_NAME = generate_name() + + create_certificate_authority( + PROJECT, LOCATION, ca_pool, CA_NAME, COMMON_NAME, ORGANIZATION, CA_DURATION + ) + + yield ca_pool, CA_NAME + + delete_certificate_authority(PROJECT, LOCATION, ca_pool, CA_NAME) diff --git a/privateca/snippets/create_ca_pool.py b/privateca/snippets/create_ca_pool.py new file mode 100644 index 000000000000..2b11785b039b --- /dev/null +++ b/privateca/snippets/create_ca_pool.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_create_ca_pool] +import google.cloud.security.privateca_v1 as privateca_v1 + + +def create_ca_pool(project_id: str, location: str, ca_pool_name: str) -> None: + """ + Create a Certificate Authority pool. All certificates created under this CA pool will + follow the same issuance policy, IAM policies,etc., + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: a unique name for the ca pool. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + ca_pool = privateca_v1.CaPool( + # Set the tier (see: https://cloud.google.com/certificate-authority-service/docs/tiers). + tier=privateca_v1.CaPool.Tier.ENTERPRISE, + ) + location_path = caServiceClient.common_location_path(project_id, location) + + # Create the pool request. + request = privateca_v1.CreateCaPoolRequest( + parent=location_path, + ca_pool_id=ca_pool_name, + ca_pool=ca_pool, + ) + + # Create the CA pool. + operation = caServiceClient.create_ca_pool(request=request) + + print("Operation result:", operation.result()) + + +# [END privateca_create_ca_pool] diff --git a/privateca/snippets/create_certificate.py b/privateca/snippets/create_certificate.py new file mode 100644 index 000000000000..9ec31ac14248 --- /dev/null +++ b/privateca/snippets/create_certificate.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_create_certificate] +from google.cloud import kms +import google.cloud.security.privateca_v1 as privateca_v1 +from google.protobuf import duration_pb2 + + +def create_certificate( + project_id: str, + location: str, + ca_pool_name: str, + ca_name: str, + certificate_name: str, + kms_location: str, + key_ring_id: str, + key_id: str, + key_version_id: str, + common_name: str, + domain_name: str, + certificate_lifetime: int, +) -> None: + """ + Create a Certificate which is issued by the Certificate Authority present in the CA Pool. + The key used to sign the certificate is created by the Cloud KMS. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: set a unique name for the CA pool. + ca_name: the name of the certificate authority which issues the certificate. + certificate_name: set a unique name for the certificate. + kms_location: Cloud KMS location. + key_ring_id: ID of the Cloud KMS key ring. + key_id: ID of the key to use. + key_version_id: verstion ID of the key to use. + common_name: a title for your certificate. + domain_name: fully qualified domain name for your certificate. + certificate_lifetime: the validity of the certificate in seconds. + """ + + kmsClient = kms.KeyManagementServiceClient() + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + # To sign and issue a certificate, a public key is essential. Here, we are making use + # of Cloud KMS to retrieve an already created public key. For more info, see: https://cloud.google.com/kms/docs/retrieve-public-key. + # Generating keys locally is also possible. + + key_version_name = kmsClient.crypto_key_version_path( + project_id, kms_location, key_ring_id, key_id, key_version_id + ) + kms_public_key = kmsClient.get_public_key(name=key_version_name) + + # Set the Public Key and its format as obtained from the Cloud KMS. + public_key = privateca_v1.PublicKey( + key=str.encode(kms_public_key.pem), + format_=privateca_v1.PublicKey.KeyFormat.PEM, + ) + + subject_config = privateca_v1.CertificateConfig.SubjectConfig( + subject=privateca_v1.Subject(common_name=common_name), + subject_alt_name=privateca_v1.SubjectAltNames(dns_names=[domain_name]), + ) + + # Set the X.509 fields required for the certificate. + x509_parameters = privateca_v1.X509Parameters( + key_usage=privateca_v1.KeyUsage( + base_key_usage=privateca_v1.KeyUsage.KeyUsageOptions( + digital_signature=True, + key_encipherment=True, + ), + extended_key_usage=privateca_v1.KeyUsage.ExtendedKeyUsageOptions( + server_auth=True, + client_auth=True, + ), + ), + ) + + # Create certificate. + certificate = privateca_v1.Certificate( + config=privateca_v1.CertificateConfig( + public_key=public_key, + subject_config=subject_config, + x509_config=x509_parameters, + ), + lifetime=duration_pb2.Duration(seconds=certificate_lifetime), + ) + + # Create the Certificate Request. + request = privateca_v1.CreateCertificateRequest( + parent=caServiceClient.ca_pool_path(project_id, location, ca_pool_name), + certificate_id=certificate_name, + certificate=certificate, + issuing_certificate_authority_id=ca_name, + ) + result = caServiceClient.create_certificate(request=request) + + print("Certificate creation result:", result) + + +# [END privateca_create_certificate] diff --git a/privateca/snippets/create_certificate_authority.py b/privateca/snippets/create_certificate_authority.py new file mode 100644 index 000000000000..2cb0c65ec17d --- /dev/null +++ b/privateca/snippets/create_certificate_authority.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_create_ca] +import google.cloud.security.privateca_v1 as privateca_v1 +from google.protobuf import duration_pb2 + + +def create_certificate_authority( + project_id: str, + location: str, + ca_pool_name: str, + ca_name: str, + common_name: str, + organization: str, + ca_duration: int, +) -> None: + """ + Create Certificate Authority which is the root CA in the given CA Pool. This CA will be + responsible for signing certificates within this pool. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: set it to the CA Pool under which the CA should be created. + ca_name: unique name for the CA. + common_name: a title for your certificate authority. + organization: the name of your company for your certificate authority. + ca_duration: the validity of the certificate authority in seconds. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + # Set the types of Algorithm used to create a cloud KMS key. + key_version_spec = privateca_v1.CertificateAuthority.KeyVersionSpec( + algorithm=privateca_v1.CertificateAuthority.SignHashAlgorithm.RSA_PKCS1_4096_SHA256 + ) + + # Set CA subject config. + subject_config = privateca_v1.CertificateConfig.SubjectConfig( + subject=privateca_v1.Subject(common_name=common_name, organization=organization) + ) + + # Set the key usage options for X.509 fields. + x509_parameters = privateca_v1.X509Parameters( + key_usage=privateca_v1.KeyUsage( + base_key_usage=privateca_v1.KeyUsage.KeyUsageOptions( + crl_sign=True, + cert_sign=True, + ) + ), + ca_options=privateca_v1.X509Parameters.CaOptions( + is_ca=True, + ), + ) + + # Set certificate authority settings. + certificate_authority = privateca_v1.CertificateAuthority( + # CertificateAuthority.Type.SELF_SIGNED denotes that this CA is a root CA. + type_=privateca_v1.CertificateAuthority.Type.SELF_SIGNED, + key_spec=key_version_spec, + config=privateca_v1.CertificateConfig( + subject_config=subject_config, + x509_config=x509_parameters, + ), + lifetime=duration_pb2.Duration(seconds=ca_duration), + ) + + ca_pool_path = caServiceClient.ca_pool_path(project_id, location, ca_pool_name) + + # Create the CertificateAuthorityRequest. + request = privateca_v1.CreateCertificateAuthorityRequest( + parent=ca_pool_path, + certificate_authority_id=ca_name, + certificate_authority=certificate_authority, + ) + + operation = caServiceClient.create_certificate_authority(request=request) + result = operation.result() + + print("Operation result:", result) + + +# [END privateca_create_ca] diff --git a/privateca/snippets/delete_ca_pool.py b/privateca/snippets/delete_ca_pool.py new file mode 100644 index 000000000000..e90f89b00f8a --- /dev/null +++ b/privateca/snippets/delete_ca_pool.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_delete_ca_pool] +import google.cloud.security.privateca_v1 as privateca_v1 + + +def delete_ca_pool(project_id: str, location: str, ca_pool_name: str) -> None: + """ + Delete the CA pool as mentioned by the ca_pool_name. + Before deleting the pool, all CAs in the pool MUST BE deleted. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: the name of the CA pool to be deleted. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + ca_pool_path = caServiceClient.ca_pool_path(project_id, location, ca_pool_name) + + # Create the Delete request. + request = privateca_v1.DeleteCaPoolRequest(name=ca_pool_path) + + # Delete the CA Pool. + caServiceClient.delete_ca_pool(request=request) + + print("Deleted CA Pool:", ca_pool_name) + + +# [END privateca_delete_ca_pool] diff --git a/privateca/snippets/delete_certificate_authority.py b/privateca/snippets/delete_certificate_authority.py new file mode 100644 index 000000000000..fc0b73e4c1cf --- /dev/null +++ b/privateca/snippets/delete_certificate_authority.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_delete_ca] +import google.cloud.security.privateca_v1 as privateca_v1 + + +def delete_certificate_authority( + project_id: str, location: str, ca_pool_name: str, ca_name: str +) -> None: + """ + Delete the Certificate Authority from the specified CA pool. + Before deletion, the CA must be disabled and must not contain any active certificates. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: the name of the CA pool under which the CA is present. + ca_name: the name of the CA to be deleted. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + ca_path = caServiceClient.certificate_authority_path( + project_id, location, ca_pool_name, ca_name + ) + + # Check if the CA is enabled. + ca_state = caServiceClient.get_certificate_authority(name=ca_path).state + print(ca_state) + if ca_state == privateca_v1.CertificateAuthority.State.ENABLED: + print( + "Please disable the Certificate Authority before deletion ! Current state:", + ca_state, + ) + + # Create the DeleteCertificateAuthorityRequest. + # Setting the ignore_active_certificates to True will delete the CA + # even if it contains active certificates. Care should be taken to re-anchor + # the certificates to new CA before deleting. + request = privateca_v1.DeleteCertificateAuthorityRequest( + name=ca_path, ignore_active_certificates=False + ) + + # Delete the Certificate Authority. + operation = caServiceClient.delete_certificate_authority(request=request) + result = operation.result() + + print("Operation result", result) + + # Get the current CA state. + ca_state = caServiceClient.get_certificate_authority(name=ca_path).state + + # Check if the CA has been deleted. + if ca_state == privateca_v1.CertificateAuthority.State.DELETED: + print("Successfully deleted Certificate Authority:", ca_name) + else: + print( + "Unable to delete Certificate Authority. Please try again ! Current state:", + ca_state, + ) + + +# [END privateca_delete_ca] diff --git a/privateca/snippets/disable_certificate_authority.py b/privateca/snippets/disable_certificate_authority.py new file mode 100644 index 000000000000..5ec4e7c0c7a5 --- /dev/null +++ b/privateca/snippets/disable_certificate_authority.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_disable_ca] +import google.cloud.security.privateca_v1 as privateca_v1 + + +def disable_certificate_authority( + project_id: str, location: str, ca_pool_name: str, ca_name: str +) -> None: + """ + Disable a Certificate Authority which is present in the given CA pool. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: the name of the CA pool under which the CA is present. + ca_name: the name of the CA to be disabled. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + ca_path = caServiceClient.certificate_authority_path( + project_id, location, ca_pool_name, ca_name + ) + + # Create the Disable Certificate Authority Request. + request = privateca_v1.DisableCertificateAuthorityRequest(name=ca_path) + + # Disable the Certificate Authority. + operation = caServiceClient.disable_certificate_authority(request=request) + result = operation.result() + + print("Operation result:", result) + + # Get the current CA state. + ca_state = caServiceClient.get_certificate_authority(name=ca_path).state + + # Check if the CA is disabled. + if ca_state == privateca_v1.CertificateAuthority.State.DISABLED: + print("Disabled Certificate Authority:", ca_name) + else: + print("Cannot disable the Certificate Authority ! Current CA State:", ca_state) + + +# [END privateca_disable_ca] diff --git a/privateca/snippets/enable_certificate_authority.py b/privateca/snippets/enable_certificate_authority.py new file mode 100644 index 000000000000..a6ecd35580bc --- /dev/null +++ b/privateca/snippets/enable_certificate_authority.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_enable_ca] +import google.cloud.security.privateca_v1 as privateca_v1 + + +def enable_certificate_authority( + project_id: str, location: str, ca_pool_name: str, ca_name: str +) -> None: + """ + Enable the Certificate Authority present in the given ca pool. + CA cannot be enabled if it has been already deleted. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: the name of the CA pool under which the CA is present. + ca_name: the name of the CA to be enabled. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + ca_path = caServiceClient.certificate_authority_path( + project_id, location, ca_pool_name, ca_name + ) + + # Create the Enable Certificate Authority Request. + request = privateca_v1.EnableCertificateAuthorityRequest( + name=ca_path, + ) + + # Enable the Certificate Authority. + operation = caServiceClient.enable_certificate_authority(request=request) + result = operation.result() + + print("Operation result:", result) + + # Get the current CA state. + ca_state = caServiceClient.get_certificate_authority(name=ca_path).state + + # Check if the CA is enabled. + if ca_state == privateca_v1.CertificateAuthority.State.ENABLED: + print("Enabled Certificate Authority:", ca_name) + else: + print("Cannot enable the Certificate Authority ! Current CA State:", ca_state) + + +# [END privateca_enable_ca] diff --git a/privateca/snippets/list_ca_pools.py b/privateca/snippets/list_ca_pools.py new file mode 100644 index 000000000000..b072045e4345 --- /dev/null +++ b/privateca/snippets/list_ca_pools.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_list_ca_pool] +import google.cloud.security.privateca_v1 as privateca_v1 + + +def list_ca_pools(project_id: str, location: str) -> None: + """ + List all CA pools present in the given project and location. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + location_path = caServiceClient.common_location_path(project_id, location) + + request = privateca_v1.ListCaPoolsRequest(parent=location_path) + + print("Available CA pools:") + + for ca_pool in caServiceClient.list_ca_pools(request=request): + ca_pool_name = ca_pool.name + # ca_pool.name represents the full resource name of the + # format 'projects/{project-id}/locations/{location}/ca-pools/{ca-pool-name}'. + # Hence stripping it down to just pool name. + print(caServiceClient.parse_ca_pool_path(ca_pool_name)["ca_pool"]) + + +# [END privateca_list_ca_pool] diff --git a/privateca/snippets/list_certificate_authorities.py b/privateca/snippets/list_certificate_authorities.py new file mode 100644 index 000000000000..19fd37d9546b --- /dev/null +++ b/privateca/snippets/list_certificate_authorities.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_list_ca] +import google.cloud.security.privateca_v1 as privateca_v1 + + +def list_certificate_authorities( + project_id: str, location: str, ca_pool_name: str +) -> None: + """ + List all Certificate authorities present in the given CA Pool. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: the name of the CA pool under which the CAs to be listed are present. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + ca_pool_path = caServiceClient.ca_pool_path(project_id, location, ca_pool_name) + + # List the CA name and its corresponding state. + for ca in caServiceClient.list_certificate_authorities(parent=ca_pool_path): + print(ca.name, "is", ca.state) + + +# [END privateca_list_ca] diff --git a/privateca/snippets/list_certificates.py b/privateca/snippets/list_certificates.py new file mode 100644 index 000000000000..9c04ed93470f --- /dev/null +++ b/privateca/snippets/list_certificates.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +# Copyright 2021 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. + +# [START privateca_list_certificate] + +import google.cloud.security.privateca_v1 as privateca_v1 + + +def list_certificates( + project_id: str, + location: str, + ca_pool_name: str, +) -> None: + """ + List Certificates present in the given CA pool. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: name of the CA pool which contains the certificates to be listed. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + ca_pool_path = caServiceClient.ca_pool_path(project_id, location, ca_pool_name) + + # Retrieve and print the certificate names. + print(f"Available certificates in CA pool {ca_pool_name}:") + for certificate in caServiceClient.list_certificates(parent=ca_pool_path): + print(certificate.name) + + +# [END privateca_list_certificate] diff --git a/privateca/snippets/noxfile.py b/privateca/snippets/noxfile.py new file mode 100644 index 000000000000..1a34a3ed3364 --- /dev/null +++ b/privateca/snippets/noxfile.py @@ -0,0 +1,258 @@ +# Copyright 2021 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 +from typing import Callable, Dict, List, Optional + +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"], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": False, + # 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": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # 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() -> Dict[str, str]: + """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] # deprecated + + # 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", "3.9"] + +# 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: str) -> List[str]: + """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: nox.sessions.Session) -> None: + if not TEST_CONFIG["enforce_type_hints"]: + session.install("flake8", "flake8-import-order") + else: + session.install("flake8", "flake8-import-order", "flake8-annotations") + + local_names = _determine_local_import_names(".") + args = FLAKE8_COMMON_ARGS + [ + "--application-import-names", + ",".join(local_names), + ".", + ] + session.run("flake8", *args) + + +# +# Black +# + + +@nox.session +def blacken(session: nox.sessions.Session) -> None: + session.install("black") + python_files = [path for path in os.listdir(".") if path.endswith(".py")] + + session.run("black", *python_files) + + +# +# Sample Tests +# + + +PYTEST_COMMON_ARGS = ["--junitxml=sponge_log.xml"] + + +def _session_tests( + session: nox.sessions.Session, post_install: Callable = None +) -> None: + if TEST_CONFIG["pip_version_override"]: + pip_version = TEST_CONFIG["pip_version_override"] + session.install(f"pip=={pip_version}") + """Runs py.test for a particular project.""" + if os.path.exists("requirements.txt"): + if os.path.exists("constraints.txt"): + session.install("-r", "requirements.txt", "-c", "constraints.txt") + else: + session.install("-r", "requirements.txt") + + if os.path.exists("requirements-test.txt"): + if os.path.exists("constraints-test.txt"): + session.install("-r", "requirements-test.txt", "-c", "constraints-test.txt") + else: + 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: nox.sessions.Session) -> None: + """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() -> Optional[str]: + """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: nox.sessions.Session, path: str) -> None: + """(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/privateca/snippets/noxfile_config.py b/privateca/snippets/noxfile_config.py new file mode 100644 index 000000000000..4a4db8c2de30 --- /dev/null +++ b/privateca/snippets/noxfile_config.py @@ -0,0 +1,38 @@ +# Copyright 2021 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. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be inported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + "ignored_versions": ["2.7"], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": False, + # 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": "GOOGLE_CLOUD_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": {}, +} diff --git a/privateca/snippets/requirements-test.txt b/privateca/snippets/requirements-test.txt new file mode 100644 index 000000000000..95104b00d625 --- /dev/null +++ b/privateca/snippets/requirements-test.txt @@ -0,0 +1,2 @@ +pytest==6.2.4 +google-auth==1.32.1 \ No newline at end of file diff --git a/privateca/snippets/requirements.txt b/privateca/snippets/requirements.txt new file mode 100644 index 000000000000..f6e2fc3c2515 --- /dev/null +++ b/privateca/snippets/requirements.txt @@ -0,0 +1,2 @@ +google-cloud-private-ca==0.4.0 +google-cloud-kms==2.3.0 \ No newline at end of file diff --git a/privateca/snippets/revoke_certificate.py b/privateca/snippets/revoke_certificate.py new file mode 100644 index 000000000000..011999a4b92f --- /dev/null +++ b/privateca/snippets/revoke_certificate.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +# [START privateca_revoke_certificate] + +import google.cloud.security.privateca_v1 as privateca_v1 + + +def revoke_certificate( + project_id: str, + location: str, + ca_pool_name: str, + certificate_name: str, +) -> None: + """ + Revoke an issued certificate. Once revoked, the certificate will become invalid and will expire post its lifetime. + + Args: + project_id: project ID or project number of the Cloud project you want to use. + location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations. + ca_pool_name: name for the CA pool which contains the certificate. + certificate_name: name of the certificate to be revoked. + """ + + caServiceClient = privateca_v1.CertificateAuthorityServiceClient() + + # Create Certificate Path. + certificate_path = caServiceClient.certificate_path( + project_id, location, ca_pool_name, certificate_name + ) + + # Create Revoke Certificate Request and specify the appropriate revocation reason. + request = privateca_v1.RevokeCertificateRequest( + name=certificate_path, reason=privateca_v1.RevocationReason.PRIVILEGE_WITHDRAWN + ) + result = caServiceClient.revoke_certificate(request=request) + + print("Certificate revoke result:", result) + + +# [END privateca_revoke_certificate] + +if __name__ == "__main__": + revoke_certificate( + project_id=sys.argv[1], + location=sys.argv[2], + ca_pool_name=sys.argv[3], + certificate_name=sys.argv[4], + ) diff --git a/privateca/snippets/test_ca_pools.py b/privateca/snippets/test_ca_pools.py new file mode 100644 index 000000000000..2f7921c5a055 --- /dev/null +++ b/privateca/snippets/test_ca_pools.py @@ -0,0 +1,74 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import typing +import uuid + +import google.auth + +from create_ca_pool import create_ca_pool +from delete_ca_pool import delete_ca_pool +from list_ca_pools import list_ca_pools + +PROJECT = google.auth.default()[1] +LOCATION = "europe-west1" + + +def generate_name() -> str: + return "test-" + uuid.uuid4().hex[:10] + + +def test_create_ca_pool(ca_pool, capsys: typing.Any) -> None: + CA_POOL_NAME = generate_name() + + create_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + + out, _ = capsys.readouterr() + + assert re.search( + f'Operation result: name: "projects/{PROJECT}/locations/{LOCATION}/caPools/{CA_POOL_NAME}"', + out, + ) + + delete_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + + +def test_list_ca_pools(capsys: typing.Any) -> None: + CA_POOL_NAME_1 = generate_name() + CA_POOL_NAME_2 = generate_name() + + create_ca_pool(PROJECT, LOCATION, CA_POOL_NAME_1) + create_ca_pool(PROJECT, LOCATION, CA_POOL_NAME_2) + list_ca_pools(PROJECT, LOCATION) + + out, _ = capsys.readouterr() + + assert "Available CA pools:" in out + assert f"{CA_POOL_NAME_1}\n" in out + assert f"{CA_POOL_NAME_2}\n" in out + + delete_ca_pool(PROJECT, LOCATION, CA_POOL_NAME_1) + delete_ca_pool(PROJECT, LOCATION, CA_POOL_NAME_2) + + +def test_delete_ca_pool(capsys: typing.Any) -> None: + CA_POOL_NAME = generate_name() + + create_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + delete_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + + out, _ = capsys.readouterr() + + assert re.search(f"Deleted CA Pool: {CA_POOL_NAME}", out) diff --git a/privateca/snippets/test_certificate_authorities.py b/privateca/snippets/test_certificate_authorities.py new file mode 100644 index 000000000000..8716e189fe62 --- /dev/null +++ b/privateca/snippets/test_certificate_authorities.py @@ -0,0 +1,96 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import typing +import uuid + +import google.auth + +from create_ca_pool import create_ca_pool +from create_certificate_authority import create_certificate_authority +from delete_ca_pool import delete_ca_pool +from delete_certificate_authority import delete_certificate_authority +from disable_certificate_authority import disable_certificate_authority +from enable_certificate_authority import enable_certificate_authority + + +PROJECT = google.auth.default()[1] +LOCATION = "europe-west1" +COMMON_NAME = "COMMON_NAME" +ORGANIZATION = "ORGANIZATION" +CA_DURATION = 1000000 + + +def generate_name() -> str: + return "i" + uuid.uuid4().hex[:10] + + +def test_create_certificate(capsys: typing.Any) -> None: + CA_POOL_NAME = generate_name() + CA_NAME = generate_name() + + create_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + create_certificate_authority( + PROJECT, LOCATION, CA_POOL_NAME, CA_NAME, COMMON_NAME, ORGANIZATION, CA_DURATION + ) + + out, _ = capsys.readouterr() + + assert re.search( + f'Operation result: name: "projects/{PROJECT}/locations/{LOCATION}/caPools/{CA_POOL_NAME}/certificateAuthorities/{CA_NAME}"', + out, + ) + + delete_certificate_authority(PROJECT, LOCATION, CA_POOL_NAME, CA_NAME) + delete_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + + +def test_enable_and_disable_certificate_authority( + certificate_authority, capsys: typing.Any +) -> None: + CA_POOL_NAME, CA_NAME = certificate_authority + + enable_certificate_authority(PROJECT, LOCATION, CA_POOL_NAME, CA_NAME) + disable_certificate_authority(PROJECT, LOCATION, CA_POOL_NAME, CA_NAME) + + out, _ = capsys.readouterr() + + assert re.search( + f"Enabled Certificate Authority: {CA_NAME}", + out, + ) + assert re.search( + f"Disabled Certificate Authority: {CA_NAME}", + out, + ) + + +def test_delete_certificate_authority(capsys: typing.Any) -> None: + CA_POOL_NAME = generate_name() + CA_NAME = generate_name() + + create_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + create_certificate_authority( + PROJECT, LOCATION, CA_POOL_NAME, CA_NAME, COMMON_NAME, ORGANIZATION, CA_DURATION + ) + delete_certificate_authority(PROJECT, LOCATION, CA_POOL_NAME, CA_NAME) + delete_ca_pool(PROJECT, LOCATION, CA_POOL_NAME) + + out, _ = capsys.readouterr() + + assert re.search( + f"Successfully deleted Certificate Authority: {CA_NAME}", + out, + ) diff --git a/privateca/snippets/test_certificates.py b/privateca/snippets/test_certificates.py new file mode 100644 index 000000000000..354f5bce8b29 --- /dev/null +++ b/privateca/snippets/test_certificates.py @@ -0,0 +1,115 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import time +import typing +import uuid + +import google.auth +from google.cloud import kms + +from create_certificate import create_certificate +from disable_certificate_authority import disable_certificate_authority +from enable_certificate_authority import enable_certificate_authority +from revoke_certificate import revoke_certificate + + +PROJECT = google.auth.default()[1] +LOCATION = "europe-west1" +COMMON_NAME = "COMMON_NAME" +ORGANIZATION = "ORGANIZATION" +CERTIFICATE_LIFETIME = 1000000 +KEY_VERSION = 1 +DOMAIN_NAME = "domain.com" + + +def generate_name() -> str: + return "test-" + uuid.uuid4().hex[:10] + + +def test_create_and_revoke_certificate_authority( + certificate_authority, capsys: typing.Any +) -> None: + KEY_RING_ID = generate_name() + CRYPTO_KEY_ID = generate_name() + CERT_NAME = generate_name() + + CA_POOL_NAME, CA_NAME = certificate_authority + enable_certificate_authority(PROJECT, LOCATION, CA_POOL_NAME, CA_NAME) + + kms_client = kms.KeyManagementServiceClient() + + kms_location_name = kms_client.common_location_path(PROJECT, LOCATION) + + kms_client.create_key_ring( + request={ + "parent": kms_location_name, + "key_ring_id": KEY_RING_ID, + "key_ring": {}, + } + ) + + key_ring_path = kms_client.key_ring_path(PROJECT, LOCATION, KEY_RING_ID) + + purpose = kms.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN + algorithm = ( + kms.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_4096_SHA256 + ) + key = { + "purpose": purpose, + "version_template": { + "algorithm": algorithm, + }, + } + + kms_client.create_crypto_key( + request={ + "parent": key_ring_path, + "crypto_key_id": CRYPTO_KEY_ID, + "crypto_key": key, + } + ) + + # Wait while crypto key is generating + time.sleep(30) + + create_certificate( + PROJECT, + LOCATION, + CA_POOL_NAME, + CA_NAME, + CERT_NAME, + LOCATION, + KEY_RING_ID, + CRYPTO_KEY_ID, + KEY_VERSION, + COMMON_NAME, + DOMAIN_NAME, + CERTIFICATE_LIFETIME, + ) + + revoke_certificate( + PROJECT, + LOCATION, + CA_POOL_NAME, + CERT_NAME, + ) + + disable_certificate_authority(PROJECT, LOCATION, CA_POOL_NAME, CA_NAME) + + out, _ = capsys.readouterr() + + assert "Certificate creation result:" in out + assert "Certificate revoke result:" in out