Démarrage rapide : initialisation des applications clientes (C++)

Ce guide de démarrage rapide vous montre comment implémenter le modèle d’initialisation de client, utilisé par le SDK MIP C++ lors du runtime.

Remarque

Les étapes décrites dans ce démarrage rapide sont nécessaires pour toute application cliente qui utilise les kits de développement logiciel (SDK) MIP File, Policy ou Protection. Bien que ce Démarrage rapide illustre l’utilisation des kits de développement logiciel (SDK) File, ce même modèle est applicable aux clients qui utilisent les kits de développement logiciel (SDK) Policy et Protection. Suivez les Démarrages rapides restants dans l’ordre, car chacun repose sur le précédent, celui-ci étant le premier.

Prérequis

Si ce n’est déjà fait, veillez à :

  • Suivre les étapes dans Installation et configuration du kit de développement logiciel (SDK) Microsoft Information Protection. Ce Démarrage rapide « Initialisation des applications clientes » repose sur l’installation et la configuration appropriées du kit de développement logiciel (SDK).
  • Si vous le souhaitez :
    • Passez en revue Objets de profil et de moteur. Les objets de profil et de moteur sont des concepts universels requis par les clients qui utilisent les kits de développement logiciel (SDK) MIP File/Policy/Protection.
    • Passez en revue les concepts d’authentification pour voir comment l’authentification et le consentement sont implémentés par le kit de développement logiciel (SDK) et l’application cliente.
    • Passez en revue les Concepts d’observateur pour en savoir plus sur les observateurs et leur implémentation. Le kit de développement logiciel (SDK) MIP utilise le modèle d’observateur pour implémenter les notifications d’événements asynchrones.

Créer une solution et un projet Visual Studio

Tout d’abord, nous créons et configurons la solution et le projet Visual Studio initiaux sur lesquels repose l’autre build de démarrages rapides.

  1. Ouvrez Visual Studio 2017, sélectionnez le menu Fichier, puis Nouveau et Projet. Dans la boîte de dialogue Nouveau projet :

    • Dans le volet de gauche, sous Installé, Autres langages, sélectionnez Visual C++.

    • Dans le volet central, sélectionnez Application console Windows.

    • Dans le volet inférieur, mettez à jour le Nom du projet, l’Emplacement et le Nom de la solution qui le contient en conséquence.

    • Lorsque vous avez terminé, cliquez sur le bouton OK en bas à droite.

      Visual Studio solution creation

  2. Ajoutez le package NuGet du kit de développement logiciel (SDK) MIP File à votre projet :

    • Dans l’Explorateur de solutions, cliquez avec le bouton de droite sur le nœud du projet (directement sous le nœud supérieur/de la solution), puis sélectionnez Gérer les packages NuGet… :

    • Lorsque l’onglet Gestionnaire de packages NuGet s’ouvre dans la zone des onglets du groupe d’éditeurs :

      • Cliquez sur Parcourir.
      • Entrez « Microsoft.InformationProtection » dans la zone de recherche.
      • Sélectionnez le package « Microsoft.InformationProtection.File ».
      • Cliquez sur « Installer », puis sur « OK » lorsque la boîte de dialogue de confirmation Aperçu des modifications s’affiche.

      Visual Studio add NuGet package

Implémenter une classe d’observateur pour analyser les objets de profil et de moteur File

À présent, créez une implémentation de base pour une classe d’observateur de profil File, en étendant la classe mip::FileProfile::Observer du kit de développement logiciel (SDK). L’observateur est instancié et utilisé ultérieurement pour analyser le chargement de l’objet de profil File et l’ajout de l’objet de moteur au profil.

  1. Ajoutez une nouvelle classe à votre projet, ce qui génère pour vous les fichiers header/.h et implementation/.cpp :

    • Dans l’Explorateur de solutions, cliquez avec le bouton de droite sur le nœud du projet une nouvelle fois, puis sélectionnez Ajouter et Classe.

    • Dans la boîte de dialogue Ajouter une classe :

      • Dans le champ Nom de la classe, entrez « profile_observer ». Notez que les deux champs Fichier .h et Fichier .cpp sont automatiquement renseignés en fonction du nom que vous entrez.
      • Lorsque vous avez terminé, cliquez sur le bouton OK.

      Visual Studio add class

  2. Après la génération des fichiers .h et .cpp pour la classe, les deux fichiers sont ouverts sous les onglets du groupe d’éditeurs. Maintenant, mettez à jour chaque fichier pour implémenter votre nouvelle classe d’observateur :

    • Mettez à jour « profile_observer.h » en sélectionnant/supprimant la classe profile_observer générée. Ne supprimez pas les directives de préprocesseur générées à l’étape précédente (#pragma, #include). Ensuite, copiez/collez la source suivante dans le fichier, après les directives de préprocesseur existantes :

      #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;
      };
      
    • Mettez à jour « profile_observer.cpp » en sélectionnant/supprimant l’implémentation de la classe profile_observer générée. Ne supprimez pas les directives de préprocesseur générées à l’étape précédente (#pragma, #include). Ensuite, copiez/collez la source suivante dans le fichier, après les directives de préprocesseur existantes :

      #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. Utilisez éventuellement F6 (Générer la solution) pour exécuter une compilation/liaison de test de votre solution afin de vous assurer qu’elle est correctement générée avant de continuer.

Implémenter un délégué d’authentification

Le kit de développement logiciel (SDK) MIP implémente l’authentification à l’aide de l’extensibilité de la classe, ce qui fournit un mécanisme pour partager le travail d’authentification avec l’application cliente. Le client doit acquérir un jeton d’accès OAuth2 approprié et le fournir au kit de développement logiciel (SDK) MIP lors du runtime.

Maintenant, créez une implémentation pour un délégué d’authentification en étendant la classe mip::AuthDelegate du kit de développement logiciel (SDK) et en substituant/implémentant la fonction purement virtuelle mip::AuthDelegate::AcquireOAuth2Token(). Le délégué d’authentification est instancié et utilisé ultérieurement par les objets de profil File et de moteur File.

  1. En utilisant la fonctionnalité « Ajouter une classe » de Visual Studio que nous avons utilisée à l’étape 1 de la section précédente, ajoutez une autre classe à votre projet. Cette fois, entrez « auth_delegate » dans le champ Nom de la classe.

  2. Maintenant, mettez à jour chaque fichier pour implémenter votre nouvelle classe de délégué d’authentification :

    • Mettez à jour « auth_delegate.h » en remplaçant tout le code de la classe auth_delegate générée par la source suivante. Ne supprimez pas les directives de préprocesseur générées à l’étape précédente (#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;
      };
      
    • Mettez à jour « auth_delegate.cpp » en remplaçant toute l’implémentation de la classe auth_delegate générée par la source suivante. Ne supprimez pas les directives de préprocesseur générées à l’étape précédente (#pragma, #include).

      Important

      Le code d’acquisition de jeton suivant n’est pas adapté à une utilisation en production. En production, il doit être remplacé par un code qui acquiert un jeton de façon dynamique en utilisant :

      • L’appId et l’URI de réponse/redirection spécifiés dans l’inscription de votre application Microsoft Entra (l’URI de réponse/redirection doit correspondre à l’inscription de votre application)
      • L’autorité et l’URL de ressource transmises par le kit de développement logiciel (SDK) dans l’argument challenge (l’URL de la ressource doit correspondre à l’API/aux autorisations de l’inscription de votre application)
      • Les informations d’identification valides de l’application/utilisateur, où le compte correspond à l’argument identity transmis par le kit de développement logiciel (SDK). Les clients « natifs » OAuth2 doivent demander les informations d’identification de l’utilisateur et se servir du flux « code d’autorisation ». Les « clients confidentiels » OAuth2 peuvent utiliser leurs propres informations d’identification sécurisées avec le flux « informations d’identification du client » (par exemple, un service) ou demander les informations d’identification de l’utilisateur avec le flux « code d’autorisation » (par exemple, une application web).

      L’acquisition de jeton OAuth2 est un protocole complexe, généralement réalisé à l’aide d’une bibliothèque. TokenAcquireOAuth2Token() est appelé uniquement par le kit de développement logiciel (SDK) MIP, comme exigé.

      #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. Utilisez éventuellement F6 (Générer la solution) pour exécuter une compilation/liaison de test de votre solution afin de vous assurer qu’elle est correctement générée avant de continuer.

Maintenant, créez une implémentation pour un délégué de consentement en étendant la classe mip::ConsentDelegate du SDK et en substituant/implémentant la fonction purement virtuelle mip::AuthDelegate::GetUserConsent(). Le délégué de consentement est instancié et utilisé ultérieurement par les objets de profil File et de moteur File.

  1. En utilisant la fonctionnalité « Ajouter une classe » de Visual Studio que nous avons utilisée précédemment, ajoutez une autre classe à votre projet. Cette fois, entrez « consent_delegate » dans le champ Nom de la classe.

  2. Maintenant, mettez à jour chaque fichier pour implémenter votre nouvelle classe de délégué de consentement :

    • Mettez à jour « consent_delegate.h » en remplaçant tout le code de la classe consent_delegate générée par la source suivante. Ne supprimez pas les directives de préprocesseur générées à l’étape précédente (#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;
      };
      
    • Mettez à jour « consent_delegate.cpp » en remplaçant toute l’implémentation de la classe consent_delegate générée par la source suivante. Ne supprimez pas les directives de préprocesseur générées à l’étape précédente (#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. Utilisez éventuellement F6 (Générer la solution) pour exécuter une compilation/liaison de test de votre solution afin de vous assurer qu’elle est correctement générée avant de continuer.

Construire un profil et un moteur File

Comme mentionné, les objets de profil et de moteur sont requis par les clients du kit de développement logiciel (SDK) qui utilisent les API MIP. Complétez la partie de code de ce Démarrage rapide en ajoutant du code pour instancier les objets de profil et de moteur :

  1. Dans l’Explorateur de solutions, ouvrez le fichier .cpp dans votre projet qui contient l’implémentation de la méthode main(). Par défaut, il a le même nom que le projet qui le contient et que vous avez spécifié lors de la création du projet.

  2. Supprimez l’implémentation générée de main(). Ne supprimez pas les directives de préprocesseur générées par Visual Studio pendant la création du projet (#pragma, #include). Ajoutez le code suivant après chaque directive de préprocesseur :

#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. Remplacez toutes les valeurs d’espace réservé du code source que vous venez de coller en utilisant des constantes de chaîne :

    Paramètre substituable Valeur Exemple
    <application-id> ID d’application (GUID) Microsoft Entra attribué à l’application inscrite à l’étape n°2 de l’article « Installation et configuration du kit de développement logiciel (SDK) MIP ». Remplacez 2 instances. "0edbblll-8773-44de-b87c-b8c6276d41eb"
    <application-name> Nom convivial défini par l’utilisateur pour votre application. Doit contenir des caractères ASCII valides (à l’exception de « ; ») et dans l’idéal correspondre au nom d’application que vous avez utilisé dans votre inscription Microsoft Entra. "AppInitialization"
    <application-version> Version définie par l’utilisateur pour votre application. Doit contenir des caractères ASCII valides (à l’exception de « ; »). "1.1.0.0"
    <engine-account> Compte utilisé pour l’identité du moteur. Lorsque vous vous authentifiez avec un compte d’utilisateur lors de l’acquisition d’un jeton, celui-ci doit correspondre à cette valeur. "user1@tenant.onmicrosoft.com"
    <engine-state> État défini par l’utilisateur à associer au moteur. "My App State"
  2. Maintenant, procédez à une dernière génération de l’application et résolvez les erreurs éventuelles. Votre code devrait être généré avec succès, mais il ne s’exécutera pas encore correctement tant que vous n’aurez pas suivi le prochain Démarrage rapide. Si vous exécutez l’application, vous voyez une sortie similaire à la suivante. Vous n’aurez pas de jeton d’accès à fournir tant que vous n’aurez pas suivi le prochain Démarrage rapide.

Étapes suivantes

Maintenant que votre code d’initialisation est terminé, vous êtes prêt pour le prochain Démarrage rapide, où vous allez commencer à utiliser les kits de développement logiciel (SDK) MIP File.