From 12010d9991c20f1b1767442c8ef9d024f9e47c00 Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Sun, 28 Sep 2025 20:21:05 -0700 Subject: [PATCH 1/8] remove swagger --- .../swagger/README.md | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 sdk/confidentialledger/azure-confidentialledger/swagger/README.md diff --git a/sdk/confidentialledger/azure-confidentialledger/swagger/README.md b/sdk/confidentialledger/azure-confidentialledger/swagger/README.md deleted file mode 100644 index 2f45033fbb62..000000000000 --- a/sdk/confidentialledger/azure-confidentialledger/swagger/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Azure Confidential Ledger - -> see https://aka.ms/autorest - -### Settings - -#### Tag: confidential-ledger -These settings apply only when `--tag=confidential-ledger` is specified on the command line. -```yaml $(tag) == 'confidential-ledger' -input-file: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/confidentialledger/data-plane/Microsoft.ConfidentialLedger/preview/2024-12-09-preview/confidentialledger.json -output-folder: ../azure/confidentialledger -namespace: azure.confidentialledger -package-name: azure-confidentialledger -license-header: MICROSOFT_MIT_NO_VERSION -clear-output-folder: false -no-namespace-folders: true -title: ConfidentialLedgerClient -version-tolerant: true -package-version: 1.1.3 -python: true -``` - -#### Tag: identity-service -These settings apply only when `--tag=identity-service` is specified on the command line. -```yaml $(tag) == 'identity-service' -input-file: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/confidentialledger/data-plane/Microsoft.ConfidentialLedger/preview/2024-12-09-preview/identityservice.json -output-folder: ../azure/confidentialledger/certificate -namespace: azure.confidentialledger.certificate -package-name: azure-confidentialledger-certificate -license-header: MICROSOFT_MIT_NO_VERSION -clear-output-folder: true -no-namespace-folders: true -title: ConfidentialLedgerCertificateClient -version-tolerant: true -package-version: 1.1.3 -python: true -``` - -#### Batch execution -Batch execution allows nested generation without the parent module overwriting the child. -```yaml -batch: - - tag: identity-service - - tag: confidential-ledger -``` \ No newline at end of file From d559b9d447eccae0a06fc82b6511498d34892273 Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Sun, 28 Sep 2025 20:21:46 -0700 Subject: [PATCH 2/8] remove extra configs --- sdk/confidentialledger/azure-confidentialledger/pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml index 06a0b0184710..b40f8b3e7e6c 100644 --- a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml +++ b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml @@ -61,5 +61,3 @@ pytyped = ["py.typed"] [tool.azure-sdk-build] pyright = false -mindependency = false -latestdependency = false From db553c4737919ee04582e316ba41635ebaa328b6 Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Mon, 29 Sep 2025 18:29:32 -0700 Subject: [PATCH 3/8] wip revert package dep temporarily --- .../azure/confidentialledger/_patch.py | 18 ++++++++--------- .../azure/confidentialledger/aio/_patch.py | 20 +++++++++---------- .../azure-confidentialledger/pyproject.toml | 1 - 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py index daa4f1057497..21e0c1645745 100644 --- a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py +++ b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py @@ -14,7 +14,7 @@ from azure.core.pipeline import policies from azure.confidentialledger._client import ConfidentialLedgerClient as GeneratedClient -from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient +# from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient __all__: List[str] = [ "ConfidentialLedgerCertificateCredential", @@ -90,18 +90,18 @@ def __init__( "authentication_policy", policies.BearerTokenCredentialPolicy(credential, *credential_scopes, **kwargs), ) - if os.path.isfile(ledger_certificate_path) is False: - # We'll need to fetch the TLS certificate. + # if os.path.isfile(ledger_certificate_path) is False: + # # We'll need to fetch the TLS certificate. - identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) + # identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) - # Ledger URIs are of the form https://.confidential-ledger.azure.com. + # # Ledger URIs are of the form https://.confidential-ledger.azure.com. - ledger_id = endpoint.replace("https://", "").split(".")[0] - ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) + # ledger_id = endpoint.replace("https://", "").split(".")[0] + # ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) - with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: - outfile.write(ledger_cert["ledgerTlsCertificate"]) + # with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: + # outfile.write(ledger_cert["ledgerTlsCertificate"]) # For ConfidentialLedgerCertificateCredential, pass the path to the certificate down to the # PipelineCLient. diff --git a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py index be31c6775a38..c523d2826be2 100644 --- a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py +++ b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py @@ -20,7 +20,7 @@ # Since we can't `await` in __init__, use the sync client for the Identity Service. -from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient +# from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient from azure.confidentialledger._patch import ConfidentialLedgerCertificateCredential __all__: List[str] = [ @@ -81,21 +81,21 @@ def __init__( if auth_policy is None: auth_policy = policies.AsyncBearerTokenCredentialPolicy(credential, *credential_scopes, **kwargs) - if os.path.isfile(ledger_certificate_path) is False: - # We'll need to fetch the TLS certificate. + # if os.path.isfile(ledger_certificate_path) is False: + # # We'll need to fetch the TLS certificate. - identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) + # identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) - # Ledger URIs are of the form https://.confidential-ledger.azure.com. + # # Ledger URIs are of the form https://.confidential-ledger.azure.com. - ledger_id = endpoint.replace("https://", "").split(".")[0] + # ledger_id = endpoint.replace("https://", "").split(".")[0] - # We use the sync client here because async __init__ is not allowed. + # # We use the sync client here because async __init__ is not allowed. - ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) + # ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) - with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: - outfile.write(ledger_cert["ledgerTlsCertificate"]) + # with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: + # outfile.write(ledger_cert["ledgerTlsCertificate"]) # For ConfidentialLedgerCertificateCredential, pass the path to the certificate down to the # PipelineCLient. diff --git a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml index b40f8b3e7e6c..a4659135f229 100644 --- a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml +++ b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml @@ -35,7 +35,6 @@ dependencies = [ "azure-core>=1.35.0", "typing-extensions>=4.6.0", "cryptography>=2.1.4", - "azure-confidentialledger-certificate>=1.0.0b1", ] dynamic = [ "version", "readme" From 95c6c8a5754fa3d6cbb3ee2d4a4f6ef4423d415c Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Mon, 29 Sep 2025 19:22:05 -0700 Subject: [PATCH 4/8] update readme --- .../README.md | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/README.md b/sdk/confidentialledger/azure-confidentialledger-certificate/README.md index 6febff920881..502da1078301 100644 --- a/sdk/confidentialledger/azure-confidentialledger-certificate/README.md +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/README.md @@ -1,4 +1,4 @@ -# Confidential Ledger Certificate client library for Python +# Azure Confidential Ledger Certificate client library for Python The Confidential Ledger Certificate client library is used to retrieve the TLS certificate required for connecting to a Confidential Ledger. @@ -10,11 +10,58 @@ The Confidential Ledger Certificate client library is used to retrieve the TLS c python -m pip install azure-confidentialledger-certificate ``` -#### Prequisites +#### Prerequisites - Python 3.9 or later is required to use this package. - You need an [Azure subscription][azure_sub] to use this package. -- An existing Confidential Ledger Certificate instance. +- An existing Confidential Ledger instance. + +## Key concepts + +Clients may authenticate with a client certificate in mutual TLS instead of via an Azure Active Directory token. Use the `get_ledger_identity()` method on the `ConfidentialLedgerCertificateClient` to retrieve the certificate. + +## Examples + +Get a ledger certificate for authentication using the `ConfidentialLedgerCertificateClient` from the `azure-confidentialledger-certificate` package, save the certificate, pass the certificate path to the `ConfidentialLedgerCertificateCredential` from the `azure-confidentialledger` package, and pass the credential to the `ConfidentialLedgerClient` for authentication: + +```python +from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient +from azure.confidentialledger import ( + ConfidentialLedgerCertificateCredential, + ConfidentialLedgerClient, +) + +identity_client = ConfidentialLedgerCertificateClient() +network_identity = identity_client.get_ledger_identity( + ledger_id="my-ledger-id" +) + +ledger_tls_cert_file_name = "ledger_certificate.pem" +with open(ledger_tls_cert_file_name, "w") as cert_file: + cert_file.write(network_identity["ledgerTlsCertificate"]) + +credential = ConfidentialLedgerCertificateCredential( + certificate_path="Path to user certificate PEM file" +) +ledger_client = ConfidentialLedgerClient( + endpoint="https://my-ledger-id.confidential-ledger.azure.com", + credential=credential, + ledger_certificate_path=ledger_tls_cert_file_name +) +``` + +## Troubleshooting + +Confidential Ledger clients raise exceptions defined in [azure-core][azure_core_exceptions]. + +## Next steps + +Use the certificate retrieved using this library with the `azure-confidentialledger` package. The Azure Confidential Ledger client library has several code samples that show common scenario operations. + +### Additional Documentation + +For more extensive documentation on Azure Confidential Ledger, see the +[API reference documentation][reference_docs]. You may also read more about Microsoft Research's open-source [Confidential Consortium Framework][ccf]. ## Contributing @@ -36,9 +83,12 @@ additional questions or comments. [code_of_conduct]: https://opensource.microsoft.com/codeofconduct/ +[azure_core_exceptions]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/core/azure-core#azure-core-library-exceptions [authenticate_with_token]: https://docs.microsoft.com/azure/cognitive-services/authentication?tabs=powershell#authenticate-with-an-authentication-token [azure_identity_credentials]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#credentials [azure_identity_pip]: https://pypi.org/project/azure-identity/ [default_azure_credential]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#defaultazurecredential [pip]: https://pypi.org/project/pip/ [azure_sub]: https://azure.microsoft.com/free/ +[reference_docs]: https://aka.ms/azsdk/python/confidentialledger/ref-docs +[ccf]: https://github.com/Microsoft/CCF From bd8364f255cd4f9798912fd769a1bbe1546b4a8a Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Mon, 29 Sep 2025 19:46:41 -0700 Subject: [PATCH 5/8] fix config files --- .../azure-confidentialledger-certificate/README.md | 1 - .../azure-confidentialledger/pyproject.toml | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/README.md b/sdk/confidentialledger/azure-confidentialledger-certificate/README.md index 502da1078301..993964892c4b 100644 --- a/sdk/confidentialledger/azure-confidentialledger-certificate/README.md +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/README.md @@ -87,7 +87,6 @@ additional questions or comments. [authenticate_with_token]: https://docs.microsoft.com/azure/cognitive-services/authentication?tabs=powershell#authenticate-with-an-authentication-token [azure_identity_credentials]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#credentials [azure_identity_pip]: https://pypi.org/project/azure-identity/ -[default_azure_credential]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#defaultazurecredential [pip]: https://pypi.org/project/pip/ [azure_sub]: https://azure.microsoft.com/free/ [reference_docs]: https://aka.ms/azsdk/python/confidentialledger/ref-docs diff --git a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml index a4659135f229..63161e5ce9cb 100644 --- a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml +++ b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml @@ -41,7 +41,9 @@ dynamic = [ ] [project.urls] -repository = "https://github.com/Azure/azure-sdk-for-python" +Homepage = "https://github.com/Azure/azure-sdk-for-python" +"Bug Reports" = "https://github.com/Azure/azure-sdk-for-python/issues" +Source = "https://github.com/Azure/azure-sdk-for-python" [tool.setuptools.dynamic] version = {attr = "azure.confidentialledger._version.VERSION"} From 1e851b46704d1a21f59bce89b6029e99b73d45a3 Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Tue, 30 Sep 2025 16:16:19 -0700 Subject: [PATCH 6/8] Revert "wip revert package dep temporarily" This reverts commit db553c4737919ee04582e316ba41635ebaa328b6. --- .../azure/confidentialledger/_patch.py | 18 ++++++++--------- .../azure/confidentialledger/aio/_patch.py | 20 +++++++++---------- .../azure-confidentialledger/pyproject.toml | 1 + 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py index 21e0c1645745..daa4f1057497 100644 --- a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py +++ b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_patch.py @@ -14,7 +14,7 @@ from azure.core.pipeline import policies from azure.confidentialledger._client import ConfidentialLedgerClient as GeneratedClient -# from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient +from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient __all__: List[str] = [ "ConfidentialLedgerCertificateCredential", @@ -90,18 +90,18 @@ def __init__( "authentication_policy", policies.BearerTokenCredentialPolicy(credential, *credential_scopes, **kwargs), ) - # if os.path.isfile(ledger_certificate_path) is False: - # # We'll need to fetch the TLS certificate. + if os.path.isfile(ledger_certificate_path) is False: + # We'll need to fetch the TLS certificate. - # identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) + identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) - # # Ledger URIs are of the form https://.confidential-ledger.azure.com. + # Ledger URIs are of the form https://.confidential-ledger.azure.com. - # ledger_id = endpoint.replace("https://", "").split(".")[0] - # ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) + ledger_id = endpoint.replace("https://", "").split(".")[0] + ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) - # with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: - # outfile.write(ledger_cert["ledgerTlsCertificate"]) + with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: + outfile.write(ledger_cert["ledgerTlsCertificate"]) # For ConfidentialLedgerCertificateCredential, pass the path to the certificate down to the # PipelineCLient. diff --git a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py index c523d2826be2..be31c6775a38 100644 --- a/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py +++ b/sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_patch.py @@ -20,7 +20,7 @@ # Since we can't `await` in __init__, use the sync client for the Identity Service. -# from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient +from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient from azure.confidentialledger._patch import ConfidentialLedgerCertificateCredential __all__: List[str] = [ @@ -81,21 +81,21 @@ def __init__( if auth_policy is None: auth_policy = policies.AsyncBearerTokenCredentialPolicy(credential, *credential_scopes, **kwargs) - # if os.path.isfile(ledger_certificate_path) is False: - # # We'll need to fetch the TLS certificate. + if os.path.isfile(ledger_certificate_path) is False: + # We'll need to fetch the TLS certificate. - # identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) + identity_service_client = ConfidentialLedgerCertificateClient(**kwargs) - # # Ledger URIs are of the form https://.confidential-ledger.azure.com. + # Ledger URIs are of the form https://.confidential-ledger.azure.com. - # ledger_id = endpoint.replace("https://", "").split(".")[0] + ledger_id = endpoint.replace("https://", "").split(".")[0] - # # We use the sync client here because async __init__ is not allowed. + # We use the sync client here because async __init__ is not allowed. - # ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) + ledger_cert = identity_service_client.get_ledger_identity(ledger_id, **kwargs) - # with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: - # outfile.write(ledger_cert["ledgerTlsCertificate"]) + with open(ledger_certificate_path, "w", encoding="utf-8") as outfile: + outfile.write(ledger_cert["ledgerTlsCertificate"]) # For ConfidentialLedgerCertificateCredential, pass the path to the certificate down to the # PipelineCLient. diff --git a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml index 63161e5ce9cb..f21c0681cf8f 100644 --- a/sdk/confidentialledger/azure-confidentialledger/pyproject.toml +++ b/sdk/confidentialledger/azure-confidentialledger/pyproject.toml @@ -35,6 +35,7 @@ dependencies = [ "azure-core>=1.35.0", "typing-extensions>=4.6.0", "cryptography>=2.1.4", + "azure-confidentialledger-certificate>=1.0.0b1", ] dynamic = [ "version", "readme" From 98a17589e4227530910dade63fe3bdde275f8ea0 Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Tue, 30 Sep 2025 17:31:31 -0700 Subject: [PATCH 7/8] move tests --- .../assets.json | 6 + .../tests/_shared/__init__.py | 0 .../tests/_shared/constants.py | 74 +++++++++++++ .../tests/_shared/testcase.py | 104 ++++++++++++++++++ .../tests/conftest.py | 11 ++ .../tests/test_identity_service_client.py | 0 .../test_identity_service_client_async.py | 0 7 files changed, 195 insertions(+) create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/assets.json create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/__init__.py create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/constants.py create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/testcase.py create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/tests/conftest.py rename sdk/confidentialledger/{azure-confidentialledger => azure-confidentialledger-certificate}/tests/test_identity_service_client.py (100%) rename sdk/confidentialledger/{azure-confidentialledger => azure-confidentialledger-certificate}/tests/test_identity_service_client_async.py (100%) diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/assets.json b/sdk/confidentialledger/azure-confidentialledger-certificate/assets.json new file mode 100644 index 000000000000..6681b7d9f036 --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/assets.json @@ -0,0 +1,6 @@ +{ + "AssetsRepo": "Azure/azure-sdk-assets", + "AssetsRepoPrefixPath": "python", + "TagPrefix": "python/confidentialledger/azure-confidentialledger-certificate", + "Tag": "python/confidentialledger/azure-confidentialledger-certificate_e868943cfa" +} diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/__init__.py b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/constants.py b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/constants.py new file mode 100644 index 000000000000..fac368a9bbbf --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/constants.py @@ -0,0 +1,74 @@ +# pylint: disable=line-too-long,useless-suppression +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import os + +TEST_PROXY_CERT = os.path.abspath( + os.path.join( + os.path.dirname(__file__), "..", "..", "..", "..", "..", "eng", "common", "testproxy", "dotnet-devcert.crt" + ) +) + +# Duplicate certificate from KeyVault. +# https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/keyvault/azure-keyvault-certificates/tests/ca.crt +USER_CERTIFICATE_PUBLIC_KEY = """-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUYju9zymmCCF7rCaROzfZs0pNgmkwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTA4MjgyMjU0MTNaFw0xOTA5 +MjcyMjU0MTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQD0YrMz5atoPmTTxLtCO69kM3E97bdjJgyAVZJS9mP3 +HQyHkFNb09eDeAAzcZLR5nYXX7yweowTWVcIe3k9+Z/tUeVrAlOVe2COaIHAUZIh +jELq/u8257/8MqqbKXhsyrWNAVDyKndDgvbbgxNsUTbMoAe9BCL/5fzowsnPLaCI +MCYRaQJUySbIoTmKi11hF09CFFSkL9nvfQODFyEde6JHPWrVRse2lioPLJeC9LoU +GNNZnbqry+UbHp4vORPp6OQTqBTm1ZVWPzCuYuWUmEe27K7zghEJr/Yx0OLq9kI5 +H960CSOkdhsOTcBkORfhivSQnmOn2RnCPIEsUTzjwXNZAgMBAAGjUzBRMB0GA1Ud +DgQWBBQIAunu6y1BmFSDfFNfTnqFggB0gzAfBgNVHSMEGDAWgBQIAunu6y1BmFSD +fFNfTnqFggB0gzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAr ++RM7gbqWRXnWJwE/hV/ZI2hXAhDN4RYQ4fWMJfg/E9wcLeqqRtJhXbqpJW08IZWp +QKcWfrFcfZ3ZxVAi5Ey+iuvD2VeBf9v5RZI4c9JqswS9xG2A1x/BeGcUk1y/q9E5 +4whf5fLSJQVxK+C53yemoHPrBg8zVhLJv5SG7Uw7jcqiQvu2aHGGWPLiO7mmMPtP +qO/I+6FjXuBpNomTqM897MY3Qzg43rpoCilpOpkRtMHknfhFxt05p+Fn73Fb60ru +ZsFRA52lsEBxGmI0QmXGjwkUZFwQTXEDUWwId3VJxoHRZwv1gmHfwhkYt+mNWJDa +mU7AMDzlQRwGC8hpWJRT +-----END CERTIFICATE-----""" + +# https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/keyvault/azure-keyvault-certificates/tests/ca.key +USER_CERTIFICATE_PRIVATE_KEY = ( + "-----BEGIN RSA PRIVATE KEY-----\n" # [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Test secret that is found elsewhere in this repo")] + """MIIEpQIBAAKCAQEA9GKzM+WraD5k08S7QjuvZDNxPe23YyYMgFWSUvZj9x0Mh5BT +W9PXg3gAM3GS0eZ2F1+8sHqME1lXCHt5Pfmf7VHlawJTlXtgjmiBwFGSIYxC6v7v +Nue//DKqmyl4bMq1jQFQ8ip3Q4L224MTbFE2zKAHvQQi/+X86MLJzy2giDAmEWkC +VMkmyKE5iotdYRdPQhRUpC/Z730DgxchHXuiRz1q1UbHtpYqDyyXgvS6FBjTWZ26 +q8vlGx6eLzkT6ejkE6gU5tWVVj8wrmLllJhHtuyu84IRCa/2MdDi6vZCOR/etAkj +pHYbDk3AZDkX4Yr0kJ5jp9kZwjyBLFE848FzWQIDAQABAoIBAHrhegv5SrOy083r +mODX0/wFJcam1dRD2HtbC6UtgNxLPfaYKmH85duUJj23uMRUJkLgf6cZJ3+/J1T7 +iN4Ru0mAKWQiGlcKX2WbxMon+dtmhGtW3n90DgPIkiJMuuGxF5Kb+9CYa7mFi4ya +ntSTDYPcX6e6AcM8KGv9La4/2f0/hQKCN3jZbnQ/GqjnJdxrAV1KV0IMoNPpZmat +Sa0EZ9eiR57/xAe1OxceEt0nO7hAl+jX7tFEGvaNClKG2OMgZ+oHOxI+s9jW8DyD +wRJbd0hxUl/KXLxzyeFTBdLxB+SQtlcr4w5khyt3AvlKd4Iveqkq2FBCtfATYitt ++Ic61IUCgYEA/j4mMdo+qokzACmGJWEquC6yNoUI5aYsHTRVvX0sLpBX7MapIEwM +zHdvMEFBxw8rs7ll1xELW+dnbIZqj/ou43E3+PSgovdFGOA8kQlPpcIIutTEZQh7 +dlWzvAVZr0iO4xfXY2gFQot41fY4yRy8Q14ayo/VjQK4uKlnGqqlmwsCgYEA9hMc +FIAYpit7779tKD+O4vEkMoTkIxqSAZUuOZ5qB5UaF4Y/+MIGZUnrjJlGLnoFQmsP +CVPVMOQKV7yjg0LBadeDHEjESwHJNk0qxPSXWuXGlu01yVkqUehNumSBdnSLBmjR +jNIxPVEmW9d6+eAzIFiTkwqM9cAuLb75DL++iasCgYEAxhqzNEE0dzl0zfmNF29B +FEb+glDi/96dnRv8eywf0yCSAmNBwXLAvkmKD/WpRWxixyX9XrlfOntzMTMDsbBl +/L9pt8kVqiY2Zw3C49h3gVdR6hKD/Z3AZhKdfDJHEbfd7sHTCRgykQmQXFgBI2QK +pguboJ627atjODB3sGWrqMUCgYEA2QoJ3lsNYqM/8TpaQQGuOaSPVK+5uOyakyLN +XqzGwGFWXiFfEz2u/m+wfpZCPIQLV4WuAYAbrb+1D6WmYwPiLESVs8DKwY2Vt3tg +mc9SIC5CdqRKqIkoto264Qf82En6xXB2Q0qxe2+z8ZWhNfv1nDYEE9FeevNCx76F +VCVbHXkCgYEA4+FD1q6iwl9wsAOKFVo+W044/MhKHDsyIED3YOzeRTAWRl2w/KX0 +c5ty2KecGu0cVXoAv2YUttHsuMZfm/QdosZr9UB4CR2lmzRys3LSx6QzCkZeMb/s +QOMs6SYCPXggdXCAu9EVf5+TtYQg7aQNTTuYErlyq2g/tk3un8bHTwI= +-----END RSA PRIVATE KEY-----""" +) + +USER_CERTIFICATE = f"{USER_CERTIFICATE_PUBLIC_KEY}\n{USER_CERTIFICATE_PRIVATE_KEY}" + +USER_CERTIFICATE_THUMBPRINT = ( + "5F:23:3D:26:E2:28:88:9C:06:E0:88:21:FA:C7:B2:9A:F8:81:30:6B:F9:15:41:F2:34:05:05:44:4C:AD:5A:B5" +) diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/testcase.py b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/testcase.py new file mode 100644 index 000000000000..8291d327fa70 --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/_shared/testcase.py @@ -0,0 +1,104 @@ +import functools +import os +import tempfile + +from devtools_testutils import ( + AzureRecordedTestCase, + EnvironmentVariableLoader, +) + +from azure.confidentialledger.certificate import ( + ConfidentialLedgerCertificateClient, +) +from azure.confidentialledger.certificate.aio import ( + ConfidentialLedgerCertificateClient as ConfidentialLedgerCertificateClientAsync, +) + +from .constants import USER_CERTIFICATE + + +ConfidentialLedgerPreparer = functools.partial( + EnvironmentVariableLoader, + "confidentialledger", + confidentialledger_id="fake", + confidentialledger_endpoint="https://fake.confidential-ledger.azure.com", + confidentialledger_resource_group="fakegroup", +) + + +class ConfidentialLedgerTestCase(AzureRecordedTestCase): + @classmethod + def setup_class(cls): + """setup any state specific to the execution of the given class (which + usually contains tests). + """ + + with tempfile.NamedTemporaryFile("w", suffix=".pem", delete=False) as tls_cert_file: + cls.network_certificate_path = tls_cert_file.name + + with tempfile.NamedTemporaryFile("w", suffix=".pem", delete=False) as user_cert_file: + user_cert_file.write(USER_CERTIFICATE) + cls.user_certificate_path = user_cert_file.name + + @classmethod + def teardown_class(cls): + """teardown any state that was previously setup with a call to + setup_class. + """ + try: + os.remove(cls.user_certificate_path) + except FileNotFoundError: + pass + + if cls.network_certificate_path: + try: + os.remove(cls.network_certificate_path) + except FileNotFoundError: + pass + + def set_ledger_identity(self, confidentialledger_id: str) -> str: + """Retrieves the Confidential Ledger's TLS certificate, saving it to the object's network + certificate path as well as returning it directly. + + :param confidentialledger_id: Id of the Confidential Ledger. + :type confidentialledger_id: str + :return: The Confidential Ledger's TLS certificate. + :rtype: str + """ + client = self.create_client_from_credential( + ConfidentialLedgerCertificateClient, + credential=None, + ) + + network_identity = client.get_ledger_identity(ledger_id=confidentialledger_id) + + with open(self.network_certificate_path, "w", encoding="utf-8") as outfile: + outfile.write(network_identity["ledgerTlsCertificate"]) + + return network_identity["ledgerTlsCertificate"] + + async def set_ledger_identity_async(self, confidentialledger_id: str) -> str: + """Retrieves the Confidential Ledger's TLS certificate, saving it to the object's network + certificate path as well as returning it directly. + + An async version of this method is needed so that this request is recorded by async tests. + + :param confidentialledger_id: Id of the Confidential Ledger. + :type confidentialledger_id: str + :return: The Confidential Ledger's TLS certificate. + :rtype: str + """ + client = self.create_client_from_credential( + ConfidentialLedgerCertificateClientAsync, + credential=None, + ) + + try: + network_identity = await client.get_ledger_identity(ledger_id=confidentialledger_id) + + with open(self.network_certificate_path, "w", encoding="utf-8") as outfile: + outfile.write(network_identity["ledgerTlsCertificate"]) + + return network_identity["ledgerTlsCertificate"] + finally: + await client.close() diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/tests/conftest.py b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/conftest.py new file mode 100644 index 000000000000..4ff615f3f93f --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/conftest.py @@ -0,0 +1,11 @@ +from devtools_testutils import test_proxy, remove_batch_sanitizers + +import pytest + + +# autouse=True will trigger this fixture on each pytest run, even if it's not explicitly used by a test method +@pytest.fixture(scope="session", autouse=True) +def add_sanitizers(test_proxy): + # Remove the following sanitizers since certain fields are needed in tests and are non-sensitive: + # - AZSDK3433: $..userid + remove_batch_sanitizers(["AZSDK3433"]) diff --git a/sdk/confidentialledger/azure-confidentialledger/tests/test_identity_service_client.py b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/test_identity_service_client.py similarity index 100% rename from sdk/confidentialledger/azure-confidentialledger/tests/test_identity_service_client.py rename to sdk/confidentialledger/azure-confidentialledger-certificate/tests/test_identity_service_client.py diff --git a/sdk/confidentialledger/azure-confidentialledger/tests/test_identity_service_client_async.py b/sdk/confidentialledger/azure-confidentialledger-certificate/tests/test_identity_service_client_async.py similarity index 100% rename from sdk/confidentialledger/azure-confidentialledger/tests/test_identity_service_client_async.py rename to sdk/confidentialledger/azure-confidentialledger-certificate/tests/test_identity_service_client_async.py From b6c78eb4a6ccde37e5cfcaaa1d596498332743ca Mon Sep 17 00:00:00 2001 From: catalinaperalta Date: Tue, 30 Sep 2025 17:41:52 -0700 Subject: [PATCH 8/8] add identity samples --- .../samples/README.md | 46 ++++++++++++ .../samples/get_certificate.py | 70 ++++++++++++++++++ .../samples/get_certificate_async.py | 71 +++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/samples/README.md create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate.py create mode 100644 sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate_async.py diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/samples/README.md b/sdk/confidentialledger/azure-confidentialledger-certificate/samples/README.md new file mode 100644 index 000000000000..413a200c36e3 --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/samples/README.md @@ -0,0 +1,46 @@ +--- +page_type: sample +languages: + - python +products: + - azure + - azure-confidentialledger +urlFragment: azure-confidentialledger-certificate-samples +--- + +# Azure Confidential Ledger Certificate client library for Python Samples + +These are code samples that show common scenario operations with the Azure Confidential Ledger Certificate client library. +The async versions of the samples (the python sample files appended with `_async`) show asynchronous operations. + +## Prerequisites + +- Python 3.6 or later is required to use this package +- You need an [Azure subscription][azure_sub], and a [Azure Confidential Ledger service instance][confidential_ledger_docs] to use this package. + +## Setup + +1. Install the Azure Confidential Ledger Certificate client library for Python with [pip](https://pypi.org/project/pip/): + +```bash +pip install azure-confidentialledger-certificate +``` + +2. Clone or download this sample repository +3. Open the sample folder in Visual Studio Code or your IDE of choice. + +## Running the samples + +1. Open a terminal window and `cd` to the directory that the samples are saved in. +2. Set the environment variables specified in the sample file you wish to run. +3. Follow the usage described in the file. + +## Next Steps + +Take a look at our [API Documentation][reference_docs] for more information about the APIs that are available in the clients. + + + +[azure_sub]: https://azure.microsoft.com/free/ +[confidential_ledger_docs]: https://aka.ms/confidentialledger-servicedocs +[reference_docs]: https://aka.ms/azsdk/python/confidentialledger/ref-docs diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate.py b/sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate.py new file mode 100644 index 000000000000..f3a25eff6e88 --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate.py @@ -0,0 +1,70 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: get_certificate.py +DESCRIPTION: + This sample demonstrates how to get the certificate from the Confidential Ledger identity service. +USAGE: + python get_certificate.py + Set the environment variables with your own values before running the sample: + 1) CONFIDENTIALLEDGER_ENDPOINT - the endpoint of the Confidential Ledger. +""" + +import logging +import os +import sys +import tempfile + +from azure.confidentialledger.certificate import ( + ConfidentialLedgerCertificateClient, +) + + +logging.basicConfig(level=logging.ERROR) +LOG = logging.getLogger() + + +def main(): + # Set the values of the following environment variables before running the sample: + # CONFIDENTIALLEDGER_ENDPOINT + try: + ledger_endpoint = os.environ["CONFIDENTIALLEDGER_ENDPOINT"] + except KeyError: + LOG.error( + "Missing environment variable 'CONFIDENTIALLEDGER_ENDPOINT' - " "please set it before running the example" + ) + sys.exit(1) + + # Under the current URI format, the ledger id is the first part of the ledger endpoint. + # i.e. https://.confidential-ledger.azure.com + ledger_id = ledger_endpoint.replace("https://", "").split(".")[0] + + identity_service_client = ConfidentialLedgerCertificateClient() # type: ignore[call-arg] + ledger_certificate = identity_service_client.get_ledger_identity(ledger_id) + + # The Confidential Ledger's TLS certificate must be written to a file to be used by the + # ConfidentialLedgerClient from the azure-confidentialledger package. Here, we write it to + # a temporary file so that is is cleaned up automatically when the program exits. + with tempfile.TemporaryDirectory() as tempdir: + ledger_cert_file = os.path.join(tempdir, f"{ledger_id}.pem") + with open(ledger_cert_file, "w") as outfile: + outfile.write(ledger_certificate["ledgerTlsCertificate"]) + + print( + f"Ledger certificate has been written to {ledger_cert_file}. " + "It will be deleted when the script completes." + ) + + print( + "The certificate can be used to create a ConfidentialLedgerClient instance." + "See samples for the azure-confidentialledger package for details:" + "https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/confidentialledger/azure-confidentialledger/samples" + ) + + +if __name__ == "__main__": + main() diff --git a/sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate_async.py b/sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate_async.py new file mode 100644 index 000000000000..e01019067b66 --- /dev/null +++ b/sdk/confidentialledger/azure-confidentialledger-certificate/samples/get_certificate_async.py @@ -0,0 +1,71 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: get_certificate_async.py +DESCRIPTION: + This sample demonstrates how to get the certificate from the Confidential Ledger identity service. +USAGE: + python get_certificate_async.py + Set the environment variables with your own values before running the sample: + 1) CONFIDENTIALLEDGER_ENDPOINT - the endpoint of the Confidential Ledger. +""" + +import asyncio +import logging +import os +import sys +import tempfile + +from azure.confidentialledger.certificate.aio import ( + ConfidentialLedgerCertificateClient, +) + + +logging.basicConfig(level=logging.ERROR) +LOG = logging.getLogger() + + +async def main(): + # Set the values of the following environment variables before running the sample: + # CONFIDENTIALLEDGER_ENDPOINT + try: + ledger_endpoint = os.environ["CONFIDENTIALLEDGER_ENDPOINT"] + except KeyError: + LOG.error( + "Missing environment variable 'CONFIDENTIALLEDGER_ENDPOINT' - " "please set it before running the example" + ) + sys.exit(1) + + # Under the current URI format, the ledger id is the first part of the ledger endpoint. + # i.e. https://.confidential-ledger.azure.com + ledger_id = ledger_endpoint.replace("https://", "").split(".")[0] + + identity_service_client = ConfidentialLedgerCertificateClient() # type: ignore[call-arg] + async with identity_service_client: + ledger_certificate = await identity_service_client.get_ledger_identity(ledger_id) + + # The Confidential Ledger's TLS certificate must be written to a file to be used by the + # ConfidentialLedgerClient. Here, we write it to a temporary file so that is is cleaned up + # automatically when the program exits. + with tempfile.TemporaryDirectory() as tempdir: + ledger_cert_file = os.path.join(tempdir, f"{ledger_id}.pem") + with open(ledger_cert_file, "w") as outfile: + outfile.write(ledger_certificate["ledgerTlsCertificate"]) + + print( + f"Ledger certificate has been written to {ledger_cert_file}. " + "It will be deleted when the script completes." + ) + + print( + "The certificate can be used to create a ConfidentialLedgerClient instance." + "See samples for the azure-confidentialledger package for details:" + "https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/confidentialledger/azure-confidentialledger/samples" + ) + +if __name__ == "__main__": + asyncio.run(main())