Início rápido: inicialização do aplicativo cliente (C++)

Esse início rápido mostra como implementar o padrão de inicialização do cliente usado pelo SDK C++ da PIM no runtime.

Observação

As etapas descritas nesse início rápido são necessárias para todos os aplicativos clientes que usam os SDKs de Arquivo, de Política ou de Proteção da PIM. Embora esse guia de início rápido demonstre o uso dos SDKs de Arquivo, esse mesmo padrão é aplicável aos clientes que usam os SDKs de Política e de Proteção. Conclua os inícios rápidos restantes em ordem, pois cada um se baseia no anterior e esse é o primeiro.

Pré-requisitos

Caso ainda não tenha feito, faça o seguinte:

Criar uma solução e um projeto do Visual Studio

Primeiro, criamos e configuramos a solução e o projeto iniciais do Visual Studio, que serão a base dos outros inícios rápidos.

  1. Abra o Visual Studio 2017, selecione o menu Arquivo, Novo, Projeto. Na caixa de diálogo Novo Projeto:

    • No painel esquerdo, em Instalado, Outras Linguagens, selecione Visual C++.

    • No painel central, selecione Aplicativo de Console do Windows

    • No painel inferior, atualize corretamente o Nome e a Localização do projeto e o Nome da solução que o contém.

    • Ao terminar, clique no botão OK, no canto inferior direito.

      Visual Studio solution creation

  2. Adicione o pacote Nuget do SDK de Arquivos da PIM ao seu projeto:

    • No Gerenciador de Soluções, clique com o botão direito do mouse sobre o nó do projeto (logo abaixo do nó superior/da solução) e selecione Gerenciar pacotes NuGet...:

    • Quando a guia Gerenciador de Pacotes NuGet for aberta na área de guias do Grupo do Editor:

      • Selecione Procurar.
      • Insira "Microsoft.InformationProtection" na caixa de pesquisa.
      • Selecione o pacote "Microsoft.InformationProtection.File".
      • Clique em "Instalar", depois clique em "OK" quando a caixa de diálogo de confirmação Visualizar alterações for exibida.

      Visual Studio add NuGet package

Implementar uma classe observadora para monitorar os objetos Mecanismo e Perfil de arquivo

Agora crie uma implementação básica de uma classe observadora de Perfil de arquivo, estendendo a classe mip::FileProfile::Observer do SDK. Uma instância do observador é criada e será usada mais tarde para monitorar o carregamento do objeto Perfil de arquivo e adicionar o objeto Mecanismo ao perfil.

  1. Adicione uma nova classe ao projeto, que gera os arquivos header/.h e implementation/.cpp para você:

    • No Gerenciador de Soluções, clique com o botão direito do mouse sobre o nó do projeto novamente, selecione Adicionar e, em seguida, Classe.

    • Na caixa de diálogo Adicionar Classe:

      • No campo Nome de Classe, digite "profile_observer". Observe que os campos Arquivo .h e Arquivo .cpp serão preenchidos automaticamente, de acordo com o nome que você inserir.
      • Ao terminar, clique no botão OK.

      Visual Studio add class

  2. Após gerar os arquivos .h e .cpp para a classe, os dois arquivos serão abertos nas guias de Grupo do Editor. Agora, atualize cada arquivo para implementar sua nova classe observer:

    • Atualize "profile_observer.h", selecionando/excluindo a classe profile_observer gerada. Não remova as diretivas de pré-processador geradas pela etapa anterior (#pragma, #include). Copie a seguinte fonte e cole-a no arquivo após cada diretiva de pré-processador existente:

      #include <memory>
      #include "mip/file/file_profile.h"
      
      class ProfileObserver final : public mip::FileProfile::Observer {
      public:
           ProfileObserver() { }
           void OnLoadSuccess(const std::shared_ptr<mip::FileProfile>& profile, const std::shared_ptr<void>& context) override;
           void OnLoadFailure(const std::exception_ptr& error, const std::shared_ptr<void>& context) override;
           void OnAddEngineSuccess(const std::shared_ptr<mip::FileEngine>& engine, const std::shared_ptr<void>& context) override;
           void OnAddEngineFailure(const std::exception_ptr& error, const std::shared_ptr<void>& context) override;
      };
      
    • Atualize "profile_observer.cpp" selecionando/excluindo a implementação da classe profile_observer gerada. Não remova as diretivas de pré-processador geradas pela etapa anterior (#pragma, #include). Copie a seguinte fonte e cole-a no arquivo após cada diretiva de pré-processador existente:

      #include <future>
      
      using std::promise;
      using std::shared_ptr;
      using std::static_pointer_cast;
      using mip::FileEngine;
      using mip::FileProfile;
      
      void ProfileObserver::OnLoadSuccess(const shared_ptr<FileProfile>& profile, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileProfile>>>(context);
           promise->set_value(profile);
      }
      
      void ProfileObserver::OnLoadFailure(const std::exception_ptr& error, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileProfile>>>(context);
           promise->set_exception(error);
      }
      
      void ProfileObserver::OnAddEngineSuccess(const shared_ptr<FileEngine>& engine, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileEngine>>>(context);
           promise->set_value(engine);
      }
      
      void ProfileObserver::OnAddEngineFailure(const std::exception_ptr& error, const shared_ptr<void>& context) {
           auto promise = static_pointer_cast<std::promise<shared_ptr<FileEngine>>>(context);
           promise->set_exception(error);
      }
      
  3. Opcionalmente, use F6 (Compilar solução) para executar um teste de compilação/link da sua solução, para garantir o sucesso da compilação antes de prosseguir.

Implementar um representante de autenticação

O SDK da PIM implementa a autenticação usando a extensibilidade de classe, que oferece um mecanismo para compartilhar o trabalho de autenticação com o aplicativo cliente. O cliente precisa adquirir um token de acesso OAuth2 adequado e fornecê-lo ao SDK da PIM no runtime.

Agora, crie uma implementação para um representante de autenticação estendendo a classe mip::AuthDelegate do SDK e substituindo/implementando a função virtual pura mip::AuthDelegate::AcquireOAuth2Token(). Uma instância do representante de autenticação é criada e será usada mais tarde pelos objetos Perfil de arquivo e Mecanismo de arquivo.

  1. Usando o mesmo recurso "Adicionar Classe" do Visual Studio usado na etapa 1 da seção anterior, adicione outra classe ao seu projeto. Dessa vez, insira "auth_delegate" no campo Nome de Classe.

  2. Agora, atualize cada arquivo para implementar a nova classe do representante de autenticação:

    • Atualize "auth_delegate.h", substituindo todo o código da classe auth_delegate gerado pela fonte a seguir. Não remova as diretivas do pré-processador geradas na etapa anterior (#pragma, #include):

      #include <string>
      #include "mip/common_types.h"
      
      class AuthDelegateImpl final : public mip::AuthDelegate {
      public:
           AuthDelegateImpl() = delete;        // Prevents default constructor
      
           AuthDelegateImpl(
             const std::string& appId)         // AppID for registered AAD app
             : mAppId(appId) {};
      
           bool AcquireOAuth2Token(            // Called by MIP SDK to get a token
             const mip::Identity& identity,    // Identity of the account to be authenticated, if known
             const OAuth2Challenge& challenge, // Authority (AAD tenant issuing token), and resource (API being accessed; "aud" claim).
             OAuth2Token& token) override;     // Token handed back to MIP SDK
      
      private:
           std::string mAppId;
           std::string mToken;
           std::string mAuthority;
           std::string mResource;
      };
      
    • Atualize "auth_delegate.cpp", substituindo toda a implementação da classe auth_delegate gerada pela fonte a seguir. Não remova as diretivas de pré-processador geradas pela etapa anterior (#pragma, #include).

      Importante

      O código de aquisição de token a seguir não é adequado para uso em produção. Em produção, ele precisa ser substituído por um código que adquira um token dinamicamente usando:

      • A ID do aplicativo e o URI de resposta/redirecionamento especificados no registro de aplicativo do Microsoft Entra (o URI de resposta/redirecionamento deve corresponder ao seu registro de aplicativo)
      • A autoridade e a URL do recurso passadas pelo SDK no argumento challenge (a URL do recurso deve corresponder à API ou às permissões do registro de aplicativo)
      • Credenciais válidas de aplicativo/usuário, em que a conta corresponde ao argumento identity passado pelo SDK. Os clientes "nativos" do OAuth2 devem solicitar credenciais de usuário e usar o fluxo do "código de autorização". Os "clientes confidenciais" do OAuth2 podem usar credenciais seguras próprias com o fluxo de "credenciais do cliente" (como um serviço) ou solicitar as credenciais do usuário usando o fluxo do "código de autorização" (como um aplicativo Web).

      A aquisição do token OAuth2 é um protocolo complexo e normalmente realizado usando uma biblioteca. O método TokenAcquireOAuth2Token() é chamado apenas quando necessário pelo SDK da PIM.

      #include <iostream>
      using std::cout;
      using std::cin;
      using std::string;
      
      bool AuthDelegateImpl::AcquireOAuth2Token(const mip::Identity& identity, const OAuth2Challenge& challenge, OAuth2Token& token) 
      {
           // Acquire a token manually, reuse previous token if same authority/resource. In production, replace with token acquisition code.
           string authority = challenge.GetAuthority();
           string resource = challenge.GetResource();
           if (mToken == "" || (authority != mAuthority || resource != mResource))
           {
               cout << "\nRun the PowerShell script to generate an access token using the following values, then copy/paste it below:\n";
               cout << "Set $authority to: " + authority + "\n";
               cout << "Set $resourceUrl to: " + resource + "\n";
               cout << "Sign in with user account: " + identity.GetEmail() + "\n";
               cout << "Enter access token: ";
               cin >> mToken;
               mAuthority = authority;
               mResource = resource;
               system("pause");
           }
      
           // Pass access token back to MIP SDK
           token.SetAccessToken(mToken);
      
           // True = successful token acquisition; False = failure
           return true;
      }
      
  3. Opcionalmente, use F6 (Compilar solução) para executar um teste de compilação/link da sua solução, para garantir o sucesso da compilação antes de prosseguir.

Agora crie uma implementação para um representante de consentimento, estendendo a classe mip::ConsentDelegate do SDK e substituindo/implementando a função virtual pura mip::AuthDelegate::GetUserConsent(). Uma instância do representante de consentimento é criada e será usada mais tarde pelos objetos Perfil de arquivo e Mecanismo de arquivo.

  1. Usando o mesmo recurso "Adicionar Classe" do Visual Studio usado anteriormente, adicione outra classe ao seu projeto. Dessa vez, insira "consent_delegate" no campo Nome da Classe.

  2. Atualize cada arquivo para implementar a nova classe representante de consentimento:

    • Atualize "consent_delegate.h" substituindo todo o código da classe consent_delegate gerado pela fonte a seguir. Não remova as diretivas do pré-processador geradas na etapa anterior (#pragma, #include):

      #include "mip/common_types.h"
      #include <string>
      
      class ConsentDelegateImpl final : public mip::ConsentDelegate {
      public:
           ConsentDelegateImpl() = default;
           virtual mip::Consent GetUserConsent(const std::string& url) override;
      };
      
    • Atualize "consent_delegate.cpp", substituindo toda a implementação da classe consent_delegate gerada pela fonte a seguir. Não remova as diretivas de pré-processador geradas pela etapa anterior (#pragma, #include).

      #include <iostream>
      using mip::Consent;
      using std::string;
      
      Consent ConsentDelegateImpl::GetUserConsent(const string& url) 
      {
           // Accept the consent to connect to the url
           std::cout << "SDK will connect to: " << url << std::endl;
           return Consent::AcceptAlways;
      }
      
  3. Opcionalmente, use F6 (Compilar solução) para executar um teste de compilação/link da sua solução, para garantir o sucesso da compilação antes de prosseguir.

Construir um Perfil e um Mecanismo de arquivo

Como já foi mencionado, os objetos Perfil e Mecanismo são necessários para os clientes do SDK que usam as APIs da PIM. Conclua a parte de codificação deste início rápido adicionando código para criar uma instância para os objetos Perfil e Mecanismo:

  1. No Gerenciador de Soluções, abra o arquivo .cpp do projeto que contém a implementação do método main(). Ele usa como padrão o mesmo nome que o projeto em que está contido, que você especificou durante a criação do projeto.

  2. Remova a implementação gerada de main(). Não remova as diretivas de pré-processador geradas pelo Visual Studio durante a criação do projeto (#pragma, #include). Acrescente o seguinte código após qualquer diretiva de pré-processador:

#include "mip/mip_context.h"  
#include "auth_delegate.h"
#include "consent_delegate.h"
#include "profile_observer.h"

using std::promise;
using std::future;
using std::make_shared;
using std::shared_ptr;
using std::string;
using std::cout;
using mip::ApplicationInfo;
using mip::FileProfile;
using mip::FileEngine;

int main()
{
  // Construct/initialize objects required by the application's profile object
  // ApplicationInfo object (App ID, name, version)
  ApplicationInfo appInfo{"<application-id>",      
                          "<application-name>",
                          "<application-version>"};

  // Create MipConfiguration object.
  std::shared_ptr<mip::MipConfiguration> mipConfiguration = std::make_shared<mip::MipConfiguration>(appInfo,    
				                                                                                               "mip_data", 
                                                                                      			         mip::LogLevel::Trace, 
                                                                                                     false);


  std::shared_ptr<mip::MipContext> mMipContext = mip::MipContext::Create(mipConfiguration);

  auto profileObserver = make_shared<ProfileObserver>();                     // Observer object
  auto authDelegateImpl = make_shared<AuthDelegateImpl>("<application-id>"); // Authentication delegate object (App ID)                 
  auto consentDelegateImpl = make_shared<ConsentDelegateImpl>();             // Consent delegate object

  // Construct/initialize profile object
  FileProfile::Settings profileSettings(
                                mMipContext,
                                mip::CacheStorageType::OnDisk,
                                consentDelegateImpl,
                                profileObserver);

  // Set up promise/future connection for async profile operations; load profile asynchronously
  auto profilePromise = make_shared<promise<shared_ptr<FileProfile>>>();
  auto profileFuture = profilePromise->get_future();

  try
	  { 
		  mip::FileProfile::LoadAsync(profileSettings, profilePromise);
  }
	  catch (const std::exception& e)
	  {
		  cout << "An exception occurred... are the Settings and ApplicationInfo objects populated correctly?\n\n" << e.what() << "'\n";
			
		  system("pause");
		  return 1;
	  }
	  auto profile = profileFuture.get();

  // Construct/initialize engine object
  FileEngine::Settings engineSettings(
                                  mip::Identity("<engine-account>"), // Engine identity (account used for authentication)
                                  authDelegateImpl,		       // Token acquisition implementation
				    "<engine-state>",                  // User-defined engine state
                                  "en-US");                          // Locale (default = en-US)
                                  
  // Set the engineId for caching. 
  engineSettings.SetEngineId("<engine-account>");
  // Set up promise/future connection for async engine operations; add engine to profile asynchronously
  auto enginePromise = make_shared<promise<shared_ptr<FileEngine>>>();
  auto engineFuture = enginePromise->get_future();
  profile->AddEngineAsync(engineSettings, enginePromise);
  std::shared_ptr<FileEngine> engine; 
  try
  {
    engine = engineFuture.get();
  }
  catch (const std::exception& e)
  {
    cout << "An exception occurred... is the access token incorrect/expired?\n\n" << e.what() << "'\n";
     
    system("pause");
    return 1;
  }

  // Application shutdown. Null out profile and engine, call ReleaseAllResources();
  // Application may crash at shutdown if resources aren't properly released.
  // handler = nullptr; // This will be used in later quick starts.
  engine = nullptr;
  profile = nullptr;   
  mMipContext->ShutDown();
  mMipContext = nullptr;

  return 0;
  }
  1. Substitua todos os valores de espaço reservado existentes no código-fonte que você acabou de colar usando constantes de cadeia de caracteres:

    Espaço reservado Valor Exemplo
    <application-id> A GUID (ID de aplicativo) do Microsoft Entra atribuído ao aplicativo registrado na etapa 2 do artigo "Configuração e instalação do SDK da PIM". Substitua duas instâncias. "0edbblll-8773-44de-b87c-b8c6276d41eb"
    <application-name> Um nome amigável definido pelo usuário para o seu aplicativo. Ele precisa conter caracteres ASCII válidos (sem ';') e preferencialmente corresponder ao nome de aplicativo usado no registro do Microsoft Entra. "AppInitialization"
    <application-version> Informações de versão do aplicativo definidas pelo usuário. Deve conter caracteres ASCII válidos (sem ';'). "1.1.0.0"
    <engine-account> Conta usada para a identidade do mecanismo. Quando você faz a autenticação com uma conta de usuário durante a aquisição do token, ela deve corresponder a esse valor. "user1@tenant.onmicrosoft.com"
    <engine-state> Estado definido pelo usuário a ser associado ao mecanismo. "My App State"
  2. Agora, faça uma compilação final do aplicativo e resolva todos os erros. O código deve ser compilado com sucesso, mas ainda não será executado corretamente até que você conclua o próximo início rápido. Se você executar o aplicativo, verá uma saída conforme a seguir. Você não terá um token de acesso para fornecer até concluir o próximo início rápido.

Próximas etapas

Agora que o seu código de inicialização está completo, você está pronto para o próximo início rápido, onde começará a experimentar os SDKs de arquivo da PIM.