Compartir a través de


Biblioteca cliente de Azure Confidential Ledger para Python: versión 1.1.1

Azure Confidential Ledger proporciona un servicio para el registro en un libro de contabilidad inmutable y a prueba de alteraciones. Como parte de la cartera de computación confidencial de Azure , Azure Confidential Ledger se ejecuta en entornos de ejecución de confianza seguros basados en hardware, también conocidos como enclaves. Se basa en el marco del consorcio confidencial de Microsoft Research.

Código | fuentePaquete (PyPI) | Paquete (Conda) | Documentación | de referencia de APIDocumentación del producto

Introducción

Instalar paquetes

Instale azure-confidentialledger y azure-identity con pip:

pip install azure-identity azure-confidentialledger

azure-identity se usa para la autenticación de Azure Active Directory, como se muestra a continuación.

Requisitos previos

  • Una suscripción de Azure
  • Python 3.6 o posterior
  • Una instancia en ejecución de Azure Confidential Ledger.
  • Un usuario registrado en Confidential Ledger, normalmente asignado durante la creación de recursos de ARM , con Administrator privilegios.

Autenticar el cliente

Uso de Azure Active Directory

En este documento se muestra cómo usar DefaultAzureCredential para autenticarse en Confidential Ledger a través de Azure Active Directory. Sin embargo, ConfidentialLedgerClient acepta cualquier credencial de azure-identity . Consulte la documentación de azure-identity para más información sobre otras credenciales.

Uso de un certificado de cliente

Como alternativa a Azure Active Directory, los clientes pueden optar por usar un certificado de cliente para autenticarse a través de TLS mutuo. azure.confidentialledger.ConfidentialLedgerCertificateCredential puede utilizarse para este fin.

Creación de un cliente

DefaultAzureCredential controlará automáticamente la mayoría de los escenarios de cliente del SDK de Azure. Para empezar, establezca variables de entorno para la identidad de AAD registrada en confidential Ledger.

export AZURE_CLIENT_ID="generated app id"
export AZURE_CLIENT_SECRET="random password"
export AZURE_TENANT_ID="tenant id"

A continuación, DefaultAzureCredential podrá autenticar .ConfidentialLedgerClient

La construcción del cliente también requiere la dirección URL y el identificador de Confidential Ledger, que puede obtener desde la CLI de Azure o Azure Portal. Cuando haya recuperado esos valores, reemplace las instancias de "my-ledger-id" y "https://my-ledger-id.confidential-ledger.azure.com" en los ejemplos siguientes. También puede que tenga que reemplazar por "https://identity.confidential-ledger.core.azure.com" el nombre de host de identityServiceUri en la descripción de ARM del libro de contabilidad.

Dado que Confidential Ledgers usa certificados autofirmados generados y almacenados de forma segura en un enclave, primero se debe recuperar el certificado de firma de cada confidential Ledger Identity Service.

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

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 = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

Convenientemente, el ConfidentialLedgerClient constructor capturará el certificado TLS del libro de contabilidad (y lo escribirá en el archivo especificado) si se proporciona con un archivo inexistente. El usuario es responsable de quitar el archivo creado según sea necesario.

from azure.confidentialledger import ConfidentialLedgerClient
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path="ledger_certificate.pem"
)

# The ledger TLS certificate is written to `ledger_certificate.pem`.

Para aclarar que se usa un archivo para el certificado TLS del libro de contabilidad, los ejemplos posteriores escribirán explícitamente el certificado TLS del libro de contabilidad en un archivo.

Conceptos clave

Entradas y transacciones del libro de contabilidad

Cada escritura en Azure Confidential Ledger genera una entrada de libro de contabilidad inmutable en el servicio. Las escrituras, también conocidas como transacciones, se identifican de forma única mediante identificadores de transacción que se incrementan con cada escritura. Una vez escritas, las entradas del libro de contabilidad se pueden recuperar en cualquier momento.

Colecciones

Aunque la mayoría de los casos de uso implican solo una recopilación por Confidential Ledger, proporcionamos la característica de identificador de recopilación en caso de que se almacenen grupos de datos semántica o lógicamente diferentes en el mismo Confidential Ledger.

Las entradas del libro de contabilidad se recuperan mediante sus collectionId. Confidential Ledger siempre asume una constante y determinada collectionId por el servicio para las entradas escritas sin un collectionId especificado.

Usuarios

Los usuarios se administran directamente con Confidential Ledger en lugar de a través de Azure. Los usuarios pueden estar basados en AAD, identificados por su identificador de objeto de AAD o basado en certificados, identificados por su huella digital del certificado PEM.

Receipts

Para aplicar garantías de integridad de transacción, Azure Confidential Ledger usa una estructura de datos [Árbol merkle][merkle_tree_wiki] para registrar el hash de todos los bloques de transacciones que se anexan al libro de contabilidad inmutable. Después de confirmar una transacción de escritura, los usuarios de Azure confidential ledger pueden obtener una prueba criptográfica de Merkle, o recibo, a través de la entrada generada en un libro de contabilidad de confidential ledger para comprobar que la operación de escritura se guardó correctamente. Un recibo de transacción de escritura es una prueba de que el sistema ha confirmado la transacción correspondiente y se puede usar para comprobar que la entrada se ha anexado efectivamente al libro de contabilidad.

Consulte el siguiente artículo para obtener más información sobre los recibos de transacción de escritura de Azure Confidential Ledger.

Comprobación de recibos

Después de obtener un recibo de una transacción de escritura, los usuarios de Azure Confidential Ledger pueden comprobar el contenido del recibo capturado después de un algoritmo de verificación. El éxito de la comprobación es una prueba de que la operación de escritura asociada al recibo se anexó correctamente al libro de contabilidad inmutable.

Consulte el siguiente artículo para obtener más información sobre el proceso de verificación de los recibos de transacción de escritura de Azure Confidential Ledger.

Notificaciones de aplicación

Las aplicaciones de Azure confidential Ledger pueden adjuntar datos arbitrarios, denominados notificaciones de aplicación, para escribir transacciones. Estas notificaciones representan las acciones ejecutadas durante una operación de escritura. Cuando se adjunte a una transacción, el resumen SHA-256 del objeto de notificaciones se incluirá en el libro de contabilidad y se confirmará como parte de la transacción de escritura. Esto garantiza que el resumen esté firmado en su lugar y no se pueda alterar.

Más adelante, las notificaciones de aplicación se pueden revelar en su forma no implícita en la carga de recibo correspondiente a la misma transacción en la que se agregaron. Esto permite a los usuarios aprovechar la información del recibo para volver a calcular el mismo resumen de notificaciones adjunto e iniciado sesión por la instancia de Azure Confidential Ledger durante la transacción. El resumen de notificaciones se puede usar como parte del proceso de comprobación de recibo de transacción de escritura, lo que proporciona una forma sin conexión para que los usuarios comprueben la autenticidad total de las notificaciones registradas.

Puede encontrar más detalles sobre el formato de notificaciones de la aplicación y el algoritmo de cálculo de resumen en los vínculos siguientes:

Consulte las siguientes páginas de documentación de CCF para obtener más información sobre las notificaciones de la aplicación CCF:

Computación confidencial

La computación confidencial de Azure permite aislar y proteger los datos mientras se procesan en la nube. Azure Confidential Ledger se ejecuta en máquinas virtuales de Computación confidencial de Azure, lo que proporciona una protección de datos más sólida con el cifrado de datos en uso.

Confidential Consortium Framework

Azure Confidential Ledger se basa en el marco de consorcio confidencial (CCF) de código abierto de Microsoft Research. En ccF, las aplicaciones son administradas por un consorcio de miembros con la capacidad de enviar propuestas para modificar y controlar la operación de aplicación. En Azure Confidential Ledger, Microsoft Azure posee una identidad de miembro del operador que le permite realizar acciones de gobernanza y mantenimiento, como reemplazar nodos incorrectos en Confidential Ledger y actualizar el código de enclave.

Ejemplos

Esta sección contiene fragmentos de código que abarcan tareas comunes, entre las que se incluyen:

Anexar entrada

Los datos que deben almacenarse inmutablemente de forma inmutable se pueden guardar en Azure Confidential Ledger anexando una entrada al libro de contabilidad.

Dado que Confidential Ledger es un sistema distribuido, los errores transitorios poco frecuentes pueden provocar la pérdida de escrituras. En el caso de las entradas que se deben conservar, es aconsejable comprobar que la escritura se hizo duradera. Para escrituras menos importantes en las que se prefiere un mayor rendimiento del cliente, se puede omitir el paso de espera.

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

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 = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

post_entry_result = ledger_client.create_ledger_entry(
        {"contents": "Hello world!"}
    )
transaction_id = post_entry_result["transactionId"]

wait_poller = ledger_client.begin_wait_for_commit(transaction_id)
wait_poller.wait()
print(f'Ledger entry at transaction id {transaction_id} has been committed successfully')

Como alternativa, el cliente puede esperar confirmación al escribir una entrada de libro de contabilidad.

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

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 = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

post_poller = ledger_client.begin_create_ledger_entry(
    {"contents": "Hello world again!"}
)
new_post_result = post_poller.result()
print(
    'The new ledger entry has been committed successfully at transaction id '
    f'{new_post_result["transactionId"]}'
)

Recuperación de entradas de libro de contabilidad

La obtención de entradas de libro de contabilidad anteriores a la más reciente puede tardar algún tiempo, ya que el servicio carga entradas históricas, por lo que se proporciona un sondeo.

Las entradas del libro de contabilidad se recuperan por colección. El valor devuelto es el valor contenido en la colección especificada en el momento dado identificado por el identificador de transacción.

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

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 = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

post_poller = ledger_client.begin_create_ledger_entry(
    {"contents": "Original hello"}
)
post_result = post_poller.result()

post_transaction_id = post_result["transactionId"]

latest_entry = ledger_client.get_current_ledger_entry()
print(
    f'Current entry (transaction id = {latest_entry["transactionId"]}) '
    f'in collection {latest_entry["collectionId"]}: {latest_entry["contents"]}'
)

post_poller = ledger_client.begin_create_ledger_entry(
    {"contents": "Hello!"}
)
post_result = post_poller.result()

get_entry_poller = ledger_client.begin_get_ledger_entry(post_transaction_id)
older_entry = get_entry_poller.result()
print(
    f'Contents of {older_entry["entry"]["collectionId"]} at {post_transaction_id}: {older_entry["entry"]["contents"]}'
)

Realización de una consulta de rango

Las entradas del libro de contabilidad se pueden recuperar en un intervalo de identificadores de transacción. Las entradas solo se devolverán de la colección predeterminada o especificada.

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

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 = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

post_poller = ledger_client.begin_create_ledger_entry(
    {"contents": "First message"}
)
first_transaction_id = post_poller.result()["transactionId"]

for i in range(10):
    ledger_client.create_ledger_entry(
        {"contents": f"Message {i}"}
    )

post_poller = ledger_client.begin_create_ledger_entry(
    {"contents": "Last message"}
)
last_transaction_id = post_poller.result()["transactionId"]

ranged_result = ledger_client.list_ledger_entries(
    from_transaction_id=first_transaction_id,
    to_transaction_id=last_transaction_id,
)
for entry in ranged_result:
    print(f'Contents at {entry["transactionId"]}: {entry["contents"]}')

Administración de usuarios

Los usuarios con Administrator privilegios pueden administrar usuarios de Confidential Ledger directamente con el propio Confidential Ledger. Los roles disponibles son Reader (de solo lectura), Contributor (lectura y escritura) y Administrator (lectura, escritura y adición o eliminación de usuarios).

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

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 = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

user_id = "some AAD object id"
user = ledger_client.create_or_update_user(
    user_id, {"assignedRole": "Contributor"}
)
# A client may now be created and used with AAD credentials (i.e. AAD-issued JWT tokens) for the user identified by `user_id`.

user = ledger_client.get_user(user_id)
assert user["userId"] == user_id
assert user["assignedRole"] == "Contributor"

ledger_client.delete_user(user_id)

# For a certificate-based user, their user ID is the fingerprint for their PEM certificate.
user_id = "PEM certificate fingerprint"
user = ledger_client.create_or_update_user(
    user_id, {"assignedRole": "Reader"}
)

user = ledger_client.get_user(user_id)
assert user["userId"] == user_id
assert user["assignedRole"] == "Reader"

ledger_client.delete_user(user_id)

Uso de la autenticación de certificados

Los clientes pueden autenticarse con un certificado de cliente en TLS mutuo en lugar de a través de un token de Azure Active Directory. ConfidentialLedgerCertificateCredential se proporciona para dichos clientes.

from azure.confidentialledger import (
    ConfidentialLedgerCertificateCredential,
    ConfidentialLedgerClient,
)
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient

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
)

Comprobación de recibos de transacciones de escritura

Los clientes pueden aprovechar la biblioteca de comprobación de recibos en el SDK para comprobar los recibos de transacción de escritura emitidos por instancias de Azure Confidential Legder. La utilidad se puede usar para comprobar completamente los recibos sin conexión, ya que el algoritmo de verificación no requiere estar conectado a un libro de contabilidad confidencial ni a ningún otro servicio de Azure.

Una vez que se ha anexado una nueva entrada al libro de contabilidad (consulte este ejemplo), es posible obtener un recibo para la transacción de escritura confirmada.

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

# Replace this with the Confidential Ledger ID 
ledger_id = "my-ledger-id"

# Setup authentication
credential = DefaultAzureCredential()

# Create a Ledger Certificate client and use it to
# retrieve the service identity for our ledger
identity_client = ConfidentialLedgerCertificateClient()
network_identity = identity_client.get_ledger_identity(
    ledger_id=ledger_id
)

# Save ledger service certificate into a file for later use
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"])

# Create Confidential Ledger client
ledger_client = ConfidentialLedgerClient(
    endpoint=f"https://{ledger_id}.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

# The method begin_get_receipt returns a poller that
# we can use to wait for the receipt to be available for retrieval 
get_receipt_poller = ledger_client.begin_get_receipt(transaction_id)
get_receipt_result = get_receipt_poller.result()

print(f"Write receipt for transaction id {transaction_id} was successfully retrieved: {get_receipt_result}")

Después de capturar un recibo de una transacción de escritura, es posible llamar a la verify_receipt función para comprobar que el recibo es válido. La función puede aceptar una lista opcional de notificaciones de aplicación para comprobar en el resumen de notificaciones de recibo.

from azure.confidentialledger.receipt import (
    verify_receipt,
)

# Read contents of service certificate file saved in previous step.
with open(ledger_tls_cert_file_name, "r") as service_cert_file:
    service_cert_content = service_cert_file.read()

# Optionally read application claims, if any
application_claims = get_receipt_result.get("applicationClaims", None) 

try:
    # Verify the contents of the receipt.
    verify_receipt(get_receipt_result["receipt"], service_cert_content, application_claims=application_claims)
    print(f"Receipt for transaction id {transaction_id} successfully verified")
except ValueError:
    print(f"Receipt verification for transaction id {transaction_id} failed")

Un programa de Python de ejemplo completo que muestra cómo anexar una nueva entrada a una instancia de Confidential Ledger en ejecución, obtener un recibo para la transacción confirmada y comprobar que el contenido del recibo se puede encontrar en la carpeta samples : get_and_verify_receipt.py.

API asincrónica

Esta biblioteca incluye una API asincrónica completa compatible con Python 3.5 y versiones posteriores. Para usarlo, primero debe instalar un transporte asincrónico, como aiohttp. Consulte la documentación de azure-core para más información.

Un cliente asincrónico se obtiene de azure.confidentialledger.aio. Los métodos tienen los mismos nombres y firmas que el cliente sincrónico. Puede encontrar ejemplos aquí.

Solución de problemas

General

Los clientes de Confidential Ledger generan excepciones definidas en azure-core. Por ejemplo, si intenta obtener una transacción que no existe, ConfidentialLedgerClient genera ResourceNotFoundError:

from azure.core.exceptions import ResourceNotFoundError
from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

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 = DefaultAzureCredential()
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name
)

try:
    ledger_client.begin_get_ledger_entry(
        transaction_id="10000.100000"  # Using a very high id that probably doesn't exist in the ledger if it's relatively new.
    )
except ResourceNotFoundError as e:
    print(e.message)

Registro

Esta biblioteca usa la biblioteca de registro estándar para el registro. La información básica sobre las sesiones HTTP (direcciones URL, encabezados, etc.) se registra en el nivel INFO.

El registro detallado de nivel de DEPURACIÓN, incluidos los cuerpos de solicitud/respuesta y los encabezados no aprobados, se puede habilitar en un cliente con el logging_enable argumento :

import logging
import sys

from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.certificate import ConfidentialLedgerCertificateClient
from azure.identity import DefaultAzureCredential

# Create a logger for the 'azure' SDK
logger = logging.getLogger('azure')
logger.setLevel(logging.DEBUG)

# Configure a console output
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

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 = DefaultAzureCredential()

# This client will log detailed information about its HTTP sessions, at DEBUG level.
ledger_client = ConfidentialLedgerClient(
    endpoint="https://my-ledger-id.confidential-ledger.azure.com",
    credential=credential,
    ledger_certificate_path=ledger_tls_cert_file_name,
    logging_enable=True,
)

Igualmente, logging_enable puede habilitar el registro detallado de una sola operación, aunque no esté habilitado para el cliente:

ledger_client.get_current_ledger_entry(logging_enable=True)

Pasos siguientes

Más código de ejemplo

Estos ejemplos de código muestran operaciones de escenario comunes con la biblioteca cliente de Azure Confidential Ledger.

Escenarios frecuentes

Escenarios avanzados

Documentación adicional

Para obtener documentación más amplia sobre Azure Confidential Ledger, consulte la documentación de referencia de api. También puede leer más sobre el marco confidencial de código abierto de Microsoft Research.

Contribuciones

Este proyecto agradece las contribuciones y sugerencias. La mayoría de las contribuciones requieren que acepte un Contrato de licencia para el colaborador (CLA) que declara que tiene el derecho a concedernos y nos concede los derechos para usar su contribución. Para más detalles, visite https://cla.microsoft.com.

Cuando se envía una solicitud de incorporación de cambios, un bot de CLA determinará de forma automática si tiene que aportar un CLA y completar la PR adecuadamente (por ejemplo, la etiqueta, el comentario). Solo siga las instrucciones que le dará el bot. Solo será necesario que lo haga una vez en todos los repositorios con nuestro CLA.

El proyecto ha adoptado el Código de conducta de código abierto de Microsoft. Para obtener más información, consulte las preguntas más frecuentes del código de conducta o póngase en contacto con opencode@microsoft.com si tiene cualquier otra pregunta o comentario.