Inicio rápido: Inicialización de la aplicación cliente (C++)

En este inicio rápido, se muestra cómo implementar el patrón de inicialización de cliente, que usa el SDK de MIP para C++ en tiempo de ejecución.

Nota:

Los pasos descritos en este inicio rápido son necesarios para cualquier aplicación cliente que use los SDK de protección, directivas o archivos de MIP. Aunque en este inicio rápido se muestra el uso de los SDK de archivos, este mismo patrón se aplica a los clientes que usan los SDK de directivas y protección. Complete los inicios rápidos restantes en serie, ya que cada uno se basa en el anterior, siendo este el primero.

Requisitos previos

Si todavía no lo ha hecho, asegúrese de:

  • Completar los pasos de Instalación y configuración del SDK de Microsoft Information Protection (MIP). Este inicio rápido "Inicialización de la aplicación cliente" se basa en la instalación y configuración correctas del SDK.
  • Opcionalmente:
    • Revise Objetos de perfil y motor. Los objetos de perfil y motor son conceptos universales, necesarios para los clientes que usan los SDK de archivos, directivas o protección de MIP.
    • Revise Conceptos de autenticación para información sobre cómo el SDK y la aplicación cliente implementan la autenticación y el consentimiento.
    • Revise Conceptos de observador para más información sobre los observadores y cómo se implementan. El SDK de MIP usa el patrón de observador para implementar notificaciones de eventos asincrónicos.

Creación de una solución y un proyecto de Visual Studio

En primer lugar, se crearán y configurarán la solución y el proyecto iniciales de Visual Studio, en los que se basan los demás inicios rápidos.

  1. Abra Visual Studio 2017, seleccione el menú Archivo y, luego, Nuevo y Proyecto. En el cuadro de diálogo Nuevo proyecto:

    • En el panel de la izquierda, en Instalados, Otros lenguajes, seleccione Visual C++.

    • En el panel central, seleccione Aplicación de consola Windows.

    • En el panel inferior, actualice el Nombre y la Ubicación del proyecto, y el Nombre de la solución en la que se incluye, según corresponda.

    • Cuando termine, haga clic en el botón Aceptar en la esquina inferior derecha.

      Visual Studio solution creation

  2. Agregue el paquete Nuget para el SDK de archivos de MIP al proyecto:

    • En el Explorador de soluciones, haga clic con el botón derecho en el nodo del proyecto (directamente debajo del nodo principal o de la solución) y seleccione Administrar paquetes NuGet…:

    • Cuando se abra la pestaña Administrador de paquetes NuGet en el área de pestañas del grupo de editores:

      • Haga clic en Examinar.
      • Escriba "Microsoft.InformationProtection" en el cuadro de búsqueda.
      • Seleccione el paquete "Microsoft.InformationProtection.File".
      • Haga clic en "Instalar" y después en "Aceptar" cuando se muestre el cuadro de diálogo de confirmación Vista previa de los cambios.

      Visual Studio add NuGet package

Implementación de una clase de observador para supervisar los objetos de motor y perfil de archivo

Ahora creará una implementación básica para una clase de observador de perfil de archivo mediante la ampliación de la clase mip::FileProfile::Observer del SDK. Se crea una instancia del observador que después se usa para supervisar la carga del objeto de perfil de archivo y la incorporación del objeto de motor en el perfil.

  1. Agregue una clase nueva al proyecto, que genera de manera automática los archivos header/.h e implementation/.cpp:

    • En el Explorador de soluciones, vuelva a hacer clic con el botón derecho en el nodo del proyecto, seleccione Agregar y después Clase.

    • En el cuadro de diálogo Agregar clase:

      • En el campo Nombre de clase, escriba "profile_observer". Observe que los campos Archivo .h y Archivo .cpp se rellenan automáticamente, en función del nombre que escriba.
      • Cuando termine, haga clic en el botón Aceptar.

      Visual Studio add class

  2. Después de generar los archivos .h y .cpp para la clase, se abren en las pestañas del grupo de editores. Ahora actualice cada archivo para implementar la clase de observador nueva:

    • Actualice "profile_observer.h"; para ello, seleccione o elimine la clase profile_observer generada. No quite las directivas de preprocesador generadas por el paso anterior (#pragma, #include). Después, copie y pegue el código siguiente en el archivo, después de las directivas de preprocesador existentes:

      #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;
      };
      
    • Actualice "profile_observer.cpp"; para ello, seleccione o elimine la implementación de la clase profile_observer generada. No quite las directivas de preprocesador generadas por el paso anterior (#pragma, #include). Después, copie y pegue el código siguiente en el archivo, después de las directivas de preprocesador existentes:

      #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, presione F6 (Compilar solución) a fin de ejecutar una compilación o vínculo de prueba de la solución, para asegurarse de que se compila correctamente antes de continuar.

Implementación de un delegado de autenticación

El SDK de MIP implementa la autenticación mediante la extensibilidad de clases, que proporciona un mecanismo para compartir el trabajo de autenticación con la aplicación cliente. El cliente debe adquirir un token de acceso de OAuth2 adecuado y proporcionarlo al SDK de MIP en tiempo de ejecución.

Ahora creará una implementación para un delegado de autenticación, mediante la extensión de la clase mip::AuthDelegate del SDK y el reemplazo o implementación de la función virtual pura mip::AuthDelegate::AcquireOAuth2Token(). Se crea una instancia del delegado de autenticación, que después usan los objetos de perfil de archivo y de motor de archivo.

  1. Con la misma característica "Agregar clase" de Visual Studio que usó en el paso 1 de la sección anterior, agregue otra clase al proyecto. Esta vez, escriba "auth_delegate" en el campo Nombre de clase.

  2. Ahora actualice cada archivo para implementar la clase nueva de delegado de autenticación:

    • Actualice "auth_delegate.h"; para ello, reemplace todo el código de la clase auth_delegate generada por el código fuente siguiente. No quite las directivas de preprocesador generadas por el paso 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;
      };
      
    • Actualice "auth_delegate.cpp"; para ello, reemplace toda la implementación de la clase auth_delegate generada por el código fuente siguiente. No quite las directivas de preprocesador generadas por el paso anterior (#pragma, #include).

      Importante

      El código de adquisición de tokens siguiente no es adecuado para usarlo en producción. En producción, esto se debe reemplazar por código que adquiera un token de manera dinámica, mediante lo siguiente:

      • El valor appId y el URI de respuesta o redireccionamiento especificado en el registro de la aplicación de Azure AD (el URI de respuesta o redireccionamiento debe coincidir con el registro de la aplicación)
      • La dirección URL de autoridad y recurso que el SDK pasó en el argumento challenge (la dirección URL del recurso debe coincidir con la API o los permisos del registro de aplicación)
      • Credenciales de usuario o aplicación válidas, donde la cuenta coincide con el argumento identity que el SDK pasó. Los clientes "nativos" de OAuth2 deben solicitar credenciales de usuario y usar el flujo de "código de autorización". Los "clientes confidenciales" de OAuth2 pueden usar sus propias credenciales seguras con el flujo de "credenciales de cliente" (como un servicio), o bien solicitar credenciales de usuario mediante el flujo de "código de autorización" (como una aplicación web).

      La adquisición de tokens de OAuth2 es un protocolo complejo y se suele realizar mediante una biblioteca. A TokenAcquireOAuth2Token() solo lo llama el SDK de MIP, según sea necesario.

      #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, presione F6 (Compilar solución) a fin de ejecutar una compilación o vínculo de prueba de la solución, para asegurarse de que se compila correctamente antes de continuar.

Ahora creará una implementación para un delegado de consentimiento, mediante la extensión de la clase mip::ConsentDelegate del SDK y el reemplazo e implementación de la función virtual pura mip::AuthDelegate::GetUserConsent(). Se crea una instancia del delegado de consentimiento, que después usan los objetos de perfil de archivo y de motor de archivo.

  1. Con la misma característica "Agregar clase" de Visual Studio que ha usado antes, agregue otra clase al proyecto. Esta vez, escriba "consent_delegate" en el campo Nombre de clase.

  2. Ahora actualice cada archivo para implementar la clase de delegado de consentimiento nueva:

    • Actualice "consent_delegate.h"; para ello, reemplace todo el código de la clase consent_delegate generada por el código fuente siguiente. No quite las directivas de preprocesador generadas por el paso 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;
      };
      
    • Actualice "consent_delegate.cpp"; para ello, reemplace toda la implementación de la clase consent_delegate generada por el código fuente siguiente. No quite las directivas de preprocesador generadas por el paso 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, presione F6 (Compilar solución) a fin de ejecutar una compilación o vínculo de prueba de la solución, para asegurarse de que se compila correctamente antes de continuar.

Creación de un motor y un perfil de archivo

Como ya se mencionó, los objetos de perfil y motor son necesarios para los clientes del SDK que usan las API de MIP. Complete la parte de codificación de este inicio rápido mediante la incorporación de código para crear una instancia de los objetos de perfil y motor:

  1. En el Explorador de soluciones, abra el archivo .cpp del proyecto que contiene la implementación del método main(). De manera predeterminada, tiene el mismo nombre que el proyecto que lo contiene, que especificó al crear el proyecto.

  2. Quite la implementación generada de main(). No quite las directivas de preprocesador generadas por Visual Studio durante la creación del proyecto (#pragma, #include). Anexe el código siguiente después de las directivas de preprocesador:

#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. Reemplace por constantes de cadena todos los valores de marcador de posición en el código fuente que acaba de pegar:

    Marcador de posición Value Ejemplo
    <application-id> Id. de la de aplicación de Azure AD (GUID) asignado a la aplicación registrada en el paso 2 del artículo "Instalación y configuración del SDK de MIP". Reemplace ambas instancias. "0edbblll-8773-44de-b87c-b8c6276d41eb"
    <application-name> Nombre descriptivo de la aplicación definido por el usuario. Debe contener caracteres ASCII válidos (excluyendo ";") e, idealmente, coincide con el nombre de la aplicación que ha usado en el registro de Azure AD. "AppInitialization"
    <application-version> Información de versión definida por el usuario para la aplicación. Debe contener caracteres ASCII válidos (excluyendo ";"). "1.1.0.0"
    <engine-account> Cuenta usada para la identidad del motor. Cuando se autentique con una cuenta de usuario durante la adquisición de tokens, debe coincidir con este valor. "user1@tenant.onmicrosoft.com"
    <engine-state> Estado definido por el usuario que se asociará con el motor. "My App State"
  2. Ahora realice una compilación final de la aplicación y resuelva los errores. El código se debe compilar de manera correcta, pero todavía no se ejecutará correctamente hasta que complete el inicio rápido siguiente. Si ejecuta la aplicación, verá un resultado similar al siguiente. No tendrá un token de acceso que proporcionar hasta que complete el inicio rápido siguiente.

Pasos siguientes

Ahora que el código de inicialización está completo, ya está listo para el inicio rápido siguiente, en el que empezará a experimentar los SDK de archivos de MIP.