Share via


Biblioteca de cliente do Azure Confidential Ledger para Python – versão 1.1.1

O Azure Confidential Ledger fornece um serviço para registar num livro razão imutável e à prova de adulteração. Como parte do portefólio de Computação Confidencial do Azure , o Azure Confidential Ledger é executado em ambientes de execução fidedignos seguros baseados em hardware, também conhecidos como enclaves. Baseia-se no Framework do Consórcio Confidencial da Microsoft Research.

Código fonte | Pacote (PyPI) | Pacote (Conda) | Documentação | de referência da APIDocumentação do produto

Introdução

Instalar pacotes

Instale azure-confidentialledger e azure-identity com pip:

pip install azure-identity azure-confidentialledger

azure-identity é utilizado para a autenticação do Azure Active Directory, conforme demonstrado abaixo.

Pré-requisitos

  • Uma subscrição do Azure
  • Python 3.6 ou posterior
  • Uma instância em execução do Azure Confidential Ledger.
  • Um utilizador registado no Livro Razão Confidencial, normalmente atribuído durante a criação de recursos do ARM , com Administrator privilégios.

Autenticar o cliente

Utilizar o Azure Active Directory

Este documento demonstra a utilização de DefaultAzureCredential para autenticar no Livro Razão Confidencial através do Azure Active Directory. No entanto, ConfidentialLedgerClient aceita qualquer credencial de identidade do azure . Veja a documentação do azure-identity para obter mais informações sobre outras credenciais.

Utilizar um certificado de cliente

Como alternativa ao Azure Active Directory, os clientes podem optar por utilizar um certificado de cliente para autenticar através do TLS mútuo. azure.confidentialledger.ConfidentialLedgerCertificateCredential pode ser utilizado para esta finalidade.

Criar um cliente

DefaultAzureCredential processará automaticamente a maioria dos cenários de cliente do SDK do Azure. Para começar, defina as variáveis de ambiente para a identidade do AAD registada no seu Livro Razão Confidencial.

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

Em seguida, DefaultAzureCredential poderá autenticar o ConfidentialLedgerClient.

A construção do cliente também requer o URL e o ID do Livro Razão Confidencial, que pode obter a partir da CLI do Azure ou do Portal do Azure. Quando tiver obtido esses valores, substitua as instâncias de "my-ledger-id" e "https://my-ledger-id.confidential-ledger.azure.com" nos exemplos abaixo. Também poderá ter de substituir "https://identity.confidential-ledger.core.azure.com" pelo nome do anfitrião na identityServiceUri descrição do ARM do livro razão.

Uma vez que os Livros RazãoIs Confidenciais utilizam certificados autoassinados gerados e armazenados em segurança num enclave, o certificado de assinatura de cada Livro Razão Confidencial tem primeiro de ser obtido a partir do Serviço de Identidade Do Livro Razão Confidencial.

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, o ConfidentialLedgerClient construtor irá obter o certificado TLS do livro razão (e escrevê-lo no ficheiro especificado) se for fornecido com um ficheiro inexistente. O utilizador é responsável por remover o ficheiro criado conforme necessário.

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 deixar claro que está a ser utilizado um ficheiro para o certificado TLS do livro razão, os exemplos subsequentes escreverão explicitamente o certificado TLS de livro razão num ficheiro.

Conceitos-chave

Entradas e transações de livros razão

Cada escrita no Azure Confidential Ledger gera uma entrada de livro razão imutável no serviço. As escritas, também conhecidas como transações, são identificadas exclusivamente por IDs de transação que incrementam com cada escrita. Uma vez escritas, as entradas de livros razão podem ser obtidas em qualquer altura.

Coleções

Embora a maioria dos casos de utilização envolva apenas uma coleção por Livro Razão Confidencial, fornecemos a funcionalidade ID da coleção no caso de grupos de dados semanticamente ou logicamente diferentes precisarem de ser armazenados no mesmo Livro Razão Confidencial.

As entradas ledger são obtidas pelo respetivo collectionId. O Livro Razão Confidencial assumirá sempre uma constante, determinada collectionId pelo serviço, para entradas escritas sem uma collectionId especificação.

Utilizadores

Os utilizadores são geridos diretamente com o Livro Razão Confidencial em vez de através do Azure. Os utilizadores podem ser baseados no AAD, identificados pelo respetivo ID de objeto do AAD ou baseado em certificado, identificados pela respetiva impressão digital do certificado PEM.

Recibos

Para impor garantias de integridade de transações, um Azure Confidential Ledger utiliza uma estrutura de dados [Merkle tree][merkle_tree_wiki] para registar o hash de todos os blocos de transações que são anexados ao livro razão imutável. Após a consolidação de uma transação de escrita, os utilizadores do Azure Confidential Ledger podem obter uma prova criptográfica do Merkle ou um recibo sobre a entrada produzida num Livro Razão Confidencial para verificar se a operação de escrita foi guardada corretamente. Um recibo de transação de escrita é a prova de que o sistema cometeu a transação correspondente e pode ser utilizado para verificar se a entrada foi efetivamente anexada ao livro razão.

Veja o seguinte artigo para obter mais informações sobre os recibos de transação de escrita do Azure Confidential Ledger.

Verificação de Recibos

Depois de obter um recibo de uma transação de escrita, os utilizadores do Azure Confidential Ledger podem verificar o conteúdo do recibo obtido na sequência de um algoritmo de verificação. O êxito da verificação é a prova de que a operação de escrita associada ao recibo foi corretamente anexada ao livro razão imutável.

Veja o seguinte artigo para obter mais informações sobre o processo de verificação dos recibos de transação de escrita do Azure Confidential Ledger.

Afirmações de Aplicação

As aplicações Azure Confidential Ledger podem anexar dados arbitrários, denominados afirmações de aplicações, para escrever transações. Estas afirmações representam as ações executadas durante uma operação de escrita. Quando anexado a uma transação, o resumo SHA-256 do objeto de afirmações é incluído no livro razão e consolidado como parte da transação de escrita. Isto garante que a digestão está iniciada e não pode ser adulterada.

Posteriormente, as afirmações da aplicação podem ser reveladas no respetivo formulário não digerido no payload de recibos correspondente à mesma transação onde foram adicionadas. Isto permite que os utilizadores tirem partido das informações no recibo para recomputação das mesmas afirmações que foram anexadas e iniciadas pela instância do Azure Confidential Ledger durante a transação. O resumo das afirmações pode ser utilizado como parte do processo de verificação de recibo de transação de escrita, fornecendo uma forma offline para os utilizadores verificarem totalmente a autenticidade das afirmações registadas.

Pode encontrar mais detalhes sobre o formato de afirmações da aplicação e o algoritmo de computação digest nas seguintes ligações:

Veja as seguintes páginas de documentação do CCF para obter mais informações sobre afirmações da Aplicação CCF:

Computação confidencial

A Computação Confidencial do Azure permite-lhe isolar e proteger os seus dados enquanto estão a ser processados na cloud. O Azure Confidential Ledger é executado em máquinas virtuais de Computação Confidencial do Azure, proporcionando assim uma proteção de dados mais forte com a encriptação de dados em utilização.

Confidencial Consortium Framework

O Azure Confidential Ledger baseia-se no Framework de Consórcio Confidencial (CCF) open source da Microsoft Research. Em CCF, as aplicações são geridas por um consórcio de membros com a capacidade de submeter propostas para modificar e governar a operação de aplicação. No Azure Confidential Ledger, o Microsoft Azure detém uma identidade de membro do operador que lhe permite executar ações de governação e manutenção, como substituir nós em mau estado de funcionamento no Livro Razão Confidencial e atualizar o código do enclave.

Exemplos

Esta secção contém fragmentos de código que abrangem tarefas comuns, incluindo:

Acrescentar entrada

Os dados que precisam de ser armazenados imutavelmente de forma à prova de adulteração podem ser guardados no Azure Confidential Ledger ao acrescentar uma entrada ao livro razão.

Uma vez que o Livro Razão Confidencial é um sistema distribuído, as falhas transitórias raras podem fazer com que as escritas sejam perdidas. Para entradas que têm de ser preservadas, é aconselhável verificar se a escrita se tornou durável. Para escritas menos importantes em que o débito de cliente mais elevado é preferido, o passo de espera pode ser ignorado.

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')

Em alternativa, o cliente pode aguardar a consolidação ao escrever uma entrada de livro razão.

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"]}'
)

A obter entradas de livros razão

A obtenção de entradas de livros razão mais antigas do que as mais recentes pode demorar algum tempo, uma vez que o serviço está a carregar entradas históricas, pelo que é fornecido um poller.

As entradas de razão são obtidas por coleção. O valor devolvido é o valor contido na coleção especificada no ponto no tempo identificado pelo ID da transação.

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"]}'
)

Criar uma consulta num intervalo

As entradas de razão podem ser obtidas através de um intervalo de IDs de transação. As entradas só serão devolvidas a partir da coleção predefinida ou 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"]}')

Gerir utilizadores

Os utilizadores com Administrator privilégios podem gerir os utilizadores do Livro Razão Confidencial diretamente com o livro razão confidencial. As funções disponíveis são Reader (só de leitura), Contributor (leitura e escrita) e Administrator (ler, escrever e adicionar ou remover utilizadores).

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)

Utilizar a autenticação de certificados

Os clientes podem autenticar com um certificado de cliente no TLS mútuo em vez de através de um token do Azure Active Directory. ConfidentialLedgerCertificateCredential é fornecido para esses 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
)

Verificar recibos de transação de escrita

Os clientes podem tirar partido da biblioteca de verificação de recibos no SDK para verificar os recibos de transação de escrita emitidos pelas instâncias do Azure Confidential Legder. O utilitário pode ser utilizado para verificar totalmente os recibos offline, uma vez que o algoritmo de verificação não necessita de estar ligado a um livro razão Confidencial ou a qualquer outro serviço do Azure.

Assim que uma nova entrada tiver sido acrescentada ao livro razão (veja este exemplo), é possível obter um recibo para a transação de escrita consolidada.

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}")

Depois de obter um recibo para uma transação de escrita, é possível chamar a verify_receipt função para verificar se o recibo é válido. A função pode aceitar uma lista opcional de afirmações de aplicação para verificar o resumo das afirmações 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")

Um programa Python de exemplo completo que mostra como acrescentar uma nova entrada a uma instância do Confidential Ledger em execução, obter um recibo da transação consolidada e verificar se o conteúdo do recibo pode ser encontrado na pasta samples : get_and_verify_receipt.py.

API Assíncrona

Esta biblioteca inclui uma API assíncrona completa suportada no Python 3.5 e superior. Para utilizá-lo, primeiro tem de instalar um transporte assíncrono, como o aiohttp. Veja a documentação do azure-core para obter mais informações.

Um cliente assíncrono é obtido a partir de azure.confidentialledger.aio. Os métodos têm os mesmos nomes e assinaturas que o cliente síncrono. Podem ser encontrados exemplos aqui.

Resolução de problemas

Geral

Os clientes Confidential Ledger geram exceções definidas no azure-core. Por exemplo, se tentar obter uma transação que não existe, ConfidentialLedgerClient gera 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)

Registo

Esta biblioteca utiliza a biblioteca de registos padrão para registo. As informações básicas sobre sessões HTTP (URLs, cabeçalhos, etc.) são registadas ao nível da INFORMAÇÃO.

O registo detalhado ao nível da DEBUG, incluindo os corpos de pedido/resposta e os cabeçalhos não retotados, pode ser ativado num cliente com o 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,
)

Da mesma forma, logging_enable pode ativar o registo detalhado para uma única operação, mesmo quando não está ativado para o cliente:

ledger_client.get_current_ledger_entry(logging_enable=True)

Passos seguintes

Mais código de exemplo

Estes exemplos de código mostram operações de cenários comuns com a biblioteca de cliente Azure Confidential Ledger.

Cenários comuns

Cenários avançados

Documentação Adicional

Para obter documentação mais extensa sobre o Azure Confidential Ledger, veja a documentação de referência da API. Também pode ler mais sobre a Estrutura de Consórcio Confidencial open source da Microsoft Research.

Contribuir

Agradecemos todas as contribuições e sugestões para este projeto. A maioria das contribuições requerem que celebre um Contrato de Licença de Contribuição (CLA) no qual se declare que tem o direito de conceder e que, na verdade, concede-nos os direitos para utilizar a sua contribuição. Para mais detalhes, visite https://cla.microsoft.com.

Quando submete um pedido Pull, um bot do CLA determina automaticamente se tem de fornecer um CLA e decorar o PR de forma adequada (por exemplo, etiqueta, comentário). Só tem de seguir as instruções fornecidas pelo bot. Apenas terá de fazer isto uma vez em todos os repositórios com o nosso CLA.

Este projeto adotou o Microsoft Open Source Code of Conduct (Código de Conduta do Microsoft Open Source). Para obter mais informações, veja a Code of Conduct FAQ (FAQ do Código de Conduta) ou envie um e-mail para opencode@microsoft.com com quaisquer perguntas ou comentários adicionais.