Share via


Procédure : Utiliser l’emprunt d’identité avec WCF (traduction automatique)

Dernière modification : jeudi 3 février 2011

S’applique à : Office 2010 | Project 2010 | Project Server 2010 | SharePoint Server 2010

Dans cet article
Développement et l'utilisation de l'Application de l'emprunt d'identité basée sur WCF
Définition des autorisations pour l'emprunt d'identité
Développement de l'Application

Important

Cet article a été traduit automatiquement, voir l’avertissement. Vous pouvez consulter la version en anglais de cet article ici.

Lorsqu’une application emprunte l’identité d’un utilisateur dans Microsoft Project Server 2010, l’application agit comme si c’était l’utilisateur représenté qui l’exécutait. Pendant l’emprunt d’identité, les appels aux méthodes PSI (Project Server Interface) utilisent les autorisations globales et les autorisations de catégorie pour l’utilisateur représenté. Cet article montre comment définir des autorisations pour l’emprunt d’identité et comment développer une application de console simple pour l’emprunt d’identité à l’aide de l’interface WCF (Windows Communication Foundation) dans Project Server. (L’exemple de code de cet article est basé sur une application de test conçue par Tiger Wang, Microsoft Corporation.)

Le présent article contient les sections suivantes :

  • Développement et l'utilisation de l'Application de l'emprunt d'identité basée sur WCF

  • Définition des autorisations pour l'emprunt d'identité

  • Développement de l'Application

  • Exemple de Code complet

Remarque sur la sécuritéRemarque sur la sécurité

Lorsque vous créez des applications qui interagissent avec Project Server, vous devez limiter l'utilisation de l'emprunt d'identité. L'attribution des privilèges d'emprunt d'identité doivent être limitées à un très petit nombre d'utilisateurs, car elle permet aux utilisateurs d'agir au nom de tous les autres utilisateurs de Project Server. Pour plus d'informations, consultez la section Définition des autorisations pour l'emprunt d'identité .

Gestionnaires d'événements de Project Server exécutent sur l'ordinateur de Project Server avec les informations d'identification d'administrateur de batterie de serveurs SharePoint, ou avec le compte d'ouverture de session pour le service de 2010 de Service événements Microsoft Project Server . Dans la mesure où les gestionnaires d'événements est déjà exécutent sur le serveur Project Server back-end, pas le Project Web App front-end, vous n'utilisez pas normalement l'emprunt d'identité dans un gestionnaire d'événements. Pour plus d'informations sur les gestionnaires d'événements, consultez Procédure : Créer un gestionnaire d’événements Project Server et consigner un événement (traduction automatique).

Développement et l'utilisation de l'Application de l'emprunt d'identité basée sur WCF

Il existe des modifications dans Project Server 2010 d'emprunt d'identité, par rapport aux Microsoft Office Project Server 2007 de la manière :

  • Nous recommandons que les nouvelles applications de l'emprunt d'identité pour Project Server 2010 utilisent l'interface de service WCF, plutôt que l'interface ASMX des services Web. Avec l'interface WCF, il n'est pas nécessaire de créer une classe de service dérivée qui substitue la méthode GetWebRequest.

  • Applications pour Project Server 2010 doivent utiliser Microsoft.NET Framework 3.5.

  • Les applications de l'emprunt d'identité développées pour Office Project Server 2007 utilisent l'interface ASMX. Pour les deux exemples, consultez Using Impersonation in Project Server. Pour exécuter Project Server 2010, les applications de l'emprunt d'identité doivent être mis à jour pour utiliser le.NET Framework 3.5 et le nouveau constructeur surcharge pour la méthode PSContextInfo.

Le téléchargement du Kit de développement logiciel de Project 2010 inclut un exemple de code complet de l'application WCFImpersonationTest.

Présentation de l'application WCFImpersonationTest   L'application WCFImpersonationTest lit le compte d'utilisateur d'une personne pour emprunter l'identité. L'application obtient le GUID de l'utilisateur d'origine à l'aide de la méthode GetCurrentUserUid et le GUID de l'utilisateur pour emprunter l'identité à l'aide de la méthode ReadResources, où les appels PSI n'utilisent pas l'emprunt d'identité. Si le compte d'utilisateur est valide, l'application définit ensuite le contexte de l'emprunt d'identité et modifie l'en-tête d'opération Web dans la demande de service WCF sortant et utilise ensuite la méthode GetCurrentUserUid à nouveau. Le résultat indique que l'application emprunte l'identité de l'autre utilisateur.

L'application WCFImpersonationTest comprend une option pour définir le point de terminaison client WCF par programmation ou en utilisant le fichier app.config. Pour obtenir une explication de ces deux façons de définir des points de terminaison clients, consultez Procédure pas à pas : Développement d’applications PSI à l’aide de WCF (traduction automatique).

Pour générer l'application, vous pouvez utiliser le code de cet article et ajouter une référence à l'assembly de proxy PSI ou ajouter un fichier de service WCF pour le service de ressources. L'article Conditions préalables pour les exemples de code basés sur WCF (traduction automatique) décrit trois méthodes pour ajouter une référence pour un service WCF.

Pour ajouter une référence pour le service WCF de ressources

  • Ajoutez une référence à l'assembly de proxy de ProjectServerServices.dll pour la PSI. L'assembly de proxy est dans le téléchargement du Kit de développement logiciel de Project 2010.

  • Ajoutez la wcf.Fichier Resource.cs à partir du téléchargement du Kit de développement. Le fichier de source de proxy est dans le fichier Source.zip dans le sous-répertoire Documentation\Intellisense\WCF.

  • Ajouter la référence de service des ressources directement dans Visual Studio. La version finale de Project Server 2010 nécessite des modifications temporaires dans le fichier web.config pour l'application de Services pour Project Server.

Nous recommandons d'utiliser une des deux premières méthodes ne requièrent pas de modifier temporairement le fichier web.config de l'application de Services pour Project Server.

Multi-authentication de revendications d'utilisation   L'authentification des utilisateurs de Project Server, par l'authentification Windows ou l'authentification par formulaires, est effectuée par le biais de demandes de remboursement dans SharePoint. L'exemple de WCFImpersonationTest est conçu pour l'authentification Windows de l'utilisateur de l'application. Multi-Authentication signifie que l'application Web sur lequel le projet Web App est approvisionné prend en charge Windows et authentification par formulaires. Si tel est le cas, tout appel à un service WCF ou ASMX qui utilise l'authentification Windows échoue avec l'erreur suivante, dans la mesure où les demandes de remboursement ne peut pas déterminer quel type d'authentification utilisateur :

The server was unable to process the request due to an internal error. 
For more information about the error, either turn on Include ExceptionDetailInFaults 
(either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) 
on the server in order to send the exception information back to the client, 
or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation 
and inspect the server trace logs.

Pour résoudre le problème, tous les appels aux méthodes WCF — pour l'emprunt d'identité ou non — doit figurer au sein d'un OperationContextScope est défini pour chaque service PSI. N'imbriquez pas étendues pour plusieurs services ; par exemple, lorsque vous utilisez des appels au service des ressources et des projets, chaque ensemble d'appels doit être dans sa propre portée.

Avant d'utiliser une application de l'emprunt d'identité, un utilisateur ou groupe doit disposer des autorisations pour l'emprunt d'identité dans l'application du service SharePoint et Project Web App.

Définition des autorisations pour l'emprunt d'identité

L'utilisateur ou groupe exécute l'application doit avoir les autorisations définies dans l'application de Service de Project Server et dans le site de projet Web App.

Remarque sur la sécuritéRemarque sur la sécurité

L'octroi d'autorisations de l'emprunt d'identité généralisée contourne le modèle de sécurité de Project Server et devient un problème administratif. Voici des exemples de la façon d'éviter l'octroi de nombreux utilisateurs les autorisations nécessaires pour l'emprunt d'identité :

  • Certains scénarios qui requièrent l'emprunt d'identité dans Microsoft Office Project Server 2007, telles que la lecture et l'envoi de l'état d'une autre ressource, ne sont plus nécessaires dans Project Server 2010 avec les nouvelles méthodes ReadStatusForResource et SubmitStatusForResource. Si l'utilisateur de l'application a l'autorisation globale StatusBrokerPermission, la méthode UpdateStatus(String) pouvez mettre à jour les données d'affectation chronologiques en incluant l'attribut ResID dans l'élément Assn pour le paramètre changeXml.

  • Utiliser un gestionnaire d'événements. Gestionnaires d'événements s'exécutent sous le compte d'utilisateur pour le Service d'événements de Project Server. Ce compte d'utilisateur peut avoir des autorisations de l'emprunt d'identité.

Pour définir des autorisations dans l'application de Service de Project Server

  1. Ouvrez la page Administration centrale de SharePoint 2010 et puis cliquez sur Gérer les applications de service.

  2. Sélectionnez la ligne de l'application de Service de Project Server (Figure 1). Au lieu de cliquer sur le nom, sélectionnez la ligne en surbrillance.

  3. Dans l'onglet Applications de service , cliquez sur Autorisations.

    La figure 1. Autorisations pour l'application de Service de Project Server

    Autorisations du service Project Server

  4. Dans la boîte de dialogue Autorisations de connexion pour l'Application de Service de serveur de projet (Figure 2), ajoutez l'utilisateur ou groupe doit avoir l'autorisation d'exécuter des applications de l'emprunt d'identité. Une fois que vous cliquez sur Ajouter, et le nom d'utilisateur affiche dans la liste des déclarations, sélectionnez l'utilisateur a été ajouté dans la liste, puis sélectionnez la case à cocher Contrôle total . Sinon, l'utilisateur n'est pas ajouté lorsque vous cliquez sur OK. Contrôle total est la seule option.

    La figure 2. Ajout d'autorisations de connexion pour l'emprunt d'identité

    Ajout d’autorisations de connexion pour l’emprunt d’identité

  5. Pour vous assurer que l'utilisateur ou le groupe est ajouté, rouvrir la boîte de dialogue Autorisations de connexion pour l'Application de Service de serveur de projet et de confirmer que le nouvel utilisateur ou groupe s'affiche dans la liste des déclarations.

Pour définir des autorisations dans le site de projet Web App

  1. Dans le menu Actions du site de Project Web App, cliquez sur Autorisations de site.

  2. Sous l'onglet du ruban Outils de l'autorisationModifier , cliquez sur Accorder des autorisations.

  3. Dans la boîte de dialogue Accorder des autorisations (Figure 3), tapez le nom d'un utilisateur ou un groupe dans la zone de texte de Utilisateurs/Groupes .

  4. Accorder à l'utilisateur ou groupe une autorisation de lecture restreinte ou d'une autorisation plus élevée. Vous pouvez utiliser un groupe SharePoint ou accorder des autorisations d'un ou plusieurs directement.

    La figure 3. L'octroi d'autorisations pour le site de projet Web App

    Autorisations du site Project Web App

  5. Cliquez sur OK.

Développement de l'Application

Les procédures suivantes montrent les parties essentielles de l'application WCFImpersonationTest. Pour le code complet, consultez la section Exemple de Code complet ou le téléchargement du Kit de développement.

Notes

Vous pouvez utiliser Microsoft Visual Studio 2008 ou Microsoft Visual Studio 2010. Si vous utilisez Visual Studio 2008, vous pouvez ultérieurement ouvrir la solution dans Visual Studio 2010 mis à niveau.

Procédure 1. Pour créer la structure d'application de console

  1. Dans Visual Studio, créez une application de console Windows. Dans la boîte de dialogue Nouveau projet , dans la liste déroulante en haut de la boîte de dialogue, sélectionnez .NET Framework 3.5. Dans cet exemple, dans la zone de texte Nom , tapez WCFImpersonationTest.

  2. Dans l' Explorateur de solutions, ajoutez les références suivantes :

    • Microsoft.Office.Project.Server.Library

    • System.Runtime.Serialization

    • System.ServiceModel

    • System.ServiceModel.Web

  3. Si vous souhaitez utiliser l'assembly de proxy PSI, comme décrit dans Conditions préalables pour les exemples de code basés sur WCF (traduction automatique), ajoutez la référence ProjectServerServices. Si vous n'utilisez pas l'assembly de proxy PSI comme référence, ajoutez le fichier wcfResource.cs qui est le code source pour le service de ressource dans l'assembly de proxy. Dans les deux cas, le nom de l'espace de noms arbitraires est SvcResource. Si vous utilisez le fichier wcfResource.cs, double-cliquez sur le nom de fichier et puis cliquez sur Afficher le code pour afficher le nom de l'espace de noms.

    Notes

    Le fichier wcfResource.cs est un proxy pour le service de ressources. Il contient plusieurs classes, y compris ResourceClient, ResourceDataSet, UserDelegationDataSet et autres personnes. Lorsque vous double-cliquez sur le fichier wcfResource.cs, Visual Studio essaie d'ouvrir le concepteur pour un service WCF, ce qui entraîne l'erreur suivante : la classe ResourceDataSet peut être conçue, mais n'est pas la première classe dans le fichier. Visual Studio requiert que les concepteurs utilisent la première classe dans le fichier. Déplacez le code de classe afin qu'il s'agit de la première classe dans le fichier et essayez de recharger le concepteur.

    Vous ne devez pas vous modifications au fichier wcfResource.cs, car il est généré par SvcUtil.exe. Au lieu de cela, cliquez droit sur le fichier et cliquez sur Afficher le code.

  4. Ajoutez le code suivant à l'instruction using en haut du fichier Program.cs : using System.ServiceModel;

    L'espace de noms System.ServiceModel inclut le type de CommunicationException qui est retourné par les exceptions de service WCF.

  5. Ajoutez les variables de classe statique, la méthode ParseCommandLine, la méthode Usage et la méthode ExitApp à la classe Program, comme indiqué dans le contenu du fichier Program.cs dans la section Exemple de Code complet . La méthode RunTests et le reste de la méthode Main doivent être ajoutées après que la classe ImpersonationTest est ajoutée dans les procédures suivantes.

Il existe de nombreuses manières d'organiser les classes et membres. Dans cet article, la classe ImpersonationTest est implémentée dans deux fichiers distincts. Le fichier ImpersonationTest.cs comprend les variables de classe et les méthodes NoImpersonation, SimpleImpersonation et DisposeClients. Le fichier Utilities.cs comprend des méthodes pour définir des points de terminaison client et pour définir le contexte de l'emprunt d'identité. Utilities.cs comprend également la classe ResourceUtilities avec la méthode GetResourceUid et la méthode GetResourceName.

Procédure 2. Pour ajouter la classe ImpersonationTest

  1. Dans l' Explorateur de solutions, cliquez droit sur le projet WCFImpersonationTest, puis ajoutez une classe nommée ImpersonationTest.

  2. Modifiez la déclaration de classe pour partial class. Ajoutez les instructions de using, des variables de classe et constructeur de classe. Lorsque les points de terminaison client sont définies, la variable de classe resourceClient est utilisée pour accéder aux méthodes dans le service de ressources à travers le routeur ProjectServer.svc frontaux dans Project Web App.

    La variable de classe contextString est définie pour l'emprunt d'identité dans la méthode SetImpersonationContext (étape 5 à 3 de la procédure).

    using System;
    using System.ServiceModel;
    using System.Globalization;
    using System.ServiceModel.Web;
    using PSLibrary = Microsoft.Office.Project.Server.Library;
    using SvcResource;
    
    namespace WCFImpersonationTest
    {
        public partial class ImpersonationTest
        {
            private static ResourceClient resourceClient;   // Client of the SvcResource reference.
            private static string appUserAccount;           // Name of the application user.
            private static Guid appUserUid;                 // GUID of the application user.
            private static string impersonatedUserAccount;  // User to impersonate.
            private static Guid impersonatedUserUid;        // GUID of user to impersonate.
            private static String contextString = String.Empty; // Impersonation context.
    
            // Variables for setting the impersonation context.
            bool isWindowsUser = true;          // Use a Windows account in this test application.
            Guid trackingGuid = Guid.Empty;     // The tracking GUID for Queue operations is not used.
            Guid siteId = Guid.Empty;           // The Project Web App site ID is not used.
            CultureInfo languageCulture = null; // The language culture is not used.
            CultureInfo localeCulture = null;   // The locale culture is not used.
    
            // ImpersonationTest constructor. Set the user account and the client endpoints. 
            public ImpersonationTest(Uri uri, string user, bool useConfig)
            {
                impersonatedUserAccount = user;
    
                if (useConfig)
                    ConfigClientEndpoints();
                else
                    SetClientEndpoints(uri);
            }
            // Add the NoImpersonation, SimpleImpersonation, and DisposeClients 
            // methods here.
            }
        }
    }
    

Vous pouvez ajouter les méthodes NoImpersonation, SimpleImpersonation et DisposeClients 3 de la procédure à la fin.

La classe ImpersonationTest a besoin d'autres méthodes pour définir des points de terminaison client ou par programme à l'aide de app.config. La classe doit également des méthodes pour définir le contexte de l'emprunt d'identité. Procédure 3 montre comment ajouter des méthodes supplémentaires dans le fichier Utilities.cs.

Pour plus d'informations sur la définition des points de terminaison et à l'aide de app.config, consultez Procédure pas à pas : Développement d’applications PSI à l’aide de WCF (traduction automatique).

Procédure 3. Pour ajouter des utilitaires pour la classe ImpersonationTest

  1. Dans l' Explorateur de solutions, cliquez droit sur le projet WCFImpersonationTest, puis ajoutez une classe nommée utilitaires.

  2. Dans le fichier Utilities.cs, modifiez la déclaration de classe en public partial class ImpersonationTest et ajouter à l'aide d'instructions, comme suit :

    using System;
    using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.Globalization;
    using PSLibrary = Microsoft.Office.Project.Server.Library;
    using SvcResource;
    
    namespace WCFImpersonationTest
    {
        // Contains methods to set the client endpoints and set the impersonation context.
        public partial class ImpersonationTest
        {
        . . .
        }
    }
    
  3. Ajoutez la méthode ConfigClientEndpoints, qui crée une instance d'un objet resourceClient avec points de terminaison sont définis dans le fichier app.config. Dans cet exemple, app.config n'inclut pas un point de terminaison pour le protocole HTTPS. La variable de classe resourceClient est déclarée dans le fichier ImpersonationTest.cs.

    // Use the endpoints defined in app.config to configure the client.
    public static void ConfigClientEndpoints()
    {
        resourceClient = new ResourceClient("basicHttp_Resource");
    }
    
  4. Ajoutez la méthode SetClientEndpoints, qui définit les liaisons et les points de terminaison par programme, puis crée une instance d'un objet resourceClient avec ces paramètres.

    CustomCertificateValidation est une méthode de rappel qui valide un certificat de serveur pour HTTPS. Dans cet exemple, il retourne toujours true.

    // Add validation code for callback if using X509 certificate and HTTPS protocol. 
    private static bool CustomCertificateValidation(
        object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
    {
        return true;
    }
    
    // Programmatically set the endpoint of the Resource service client.
    public static void SetClientEndpoints(Uri pwaUri)
    {
        // Set the address of the front-end WCF router.
        const string svcRouter = "_vti_bin/PSI/ProjectServer.svc";
    
        string pwaUrl = pwaUri.Scheme + Uri.SchemeDelimiter + pwaUri.Host + ":"
            + pwaUri.Port + pwaUri.AbsolutePath;
    
        BasicHttpBinding binding = null;
    
        if (pwaUri.Scheme.Equals(Uri.UriSchemeHttps))
        {
            ServicePointManager.ServerCertificateValidationCallback +=
                new RemoteCertificateValidationCallback( 
                    CustomCertificateValidation);
    
            binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
        }
        else
        {
            binding = new BasicHttpBinding(
                BasicHttpSecurityMode.TransportCredentialOnly);
        }
    
        binding.Name = "basicHttpConf";
        binding.SendTimeout = TimeSpan.MaxValue;
        ((BasicHttpBinding)binding).MaxReceivedMessageSize = 500000000;
        ((BasicHttpBinding)binding).ReaderQuotas.MaxNameTableCharCount = 500000000;
        ((BasicHttpBinding)binding).MessageEncoding = WSMessageEncoding.Text;
        ((BasicHttpBinding)binding).Security.Transport.ClientCredentialType =
            HttpClientCredentialType.Ntlm;
    
        EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter);
    
        resourceClient = new ResourceClient(binding, address);
        resourceClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel =
            TokenImpersonationLevel.Impersonation;
        resourceClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
    }
    
  5. Ajoutez la méthode SetImpersonationContext et la méthode GetImpersonationContext à la classe TestImpersonation. Elles sont similaires aux méthodes utilisées pour les applications de l'emprunt d'identité dans Office Project Server 2007, à ceci près qu'ils utilisent les paramètres languageCulture et localeCulture pour la surcharge du constructeur PSContextInfo nouveau. Dans cet exemple, ces paramètres sont configurés pour null dans le fichier ImpersonationTest.cs.

    La variable de classe contextString est déclarée dans le fichier ImpersonationTest.cs et utilisée lors de la définition du contexte d'opération Web pour effectuer des appels PSI qui utilisent l'emprunt d'identité.

    // Set the impersonation context for calls to the PSI on behalf of 
    // the impersonated user.
    internal static void SetImpersonationContext(
                            bool isWindowsUser, String userNTAccount,
        Guid userGuid, Guid trackingGuid, Guid siteId,
        CultureInfo languageCulture, CultureInfo localeCulture)
    {
        contextString = GetImpersonationContext(
                            isWindowsUser, userNTAccount, userGuid,
            trackingGuid, siteId, languageCulture, localeCulture);
    }
    
    // Get the impersonation context.
    private static String GetImpersonationContext(
        bool isWindowsUser, String userNTAccount,
        Guid userGuid, Guid trackingGuid, Guid siteId,
        CultureInfo languageCulture, CultureInfo localeCulture)
    {
        PSLibrary.PSContextInfo contextInfo = new PSLibrary.PSContextInfo(
            isWindowsUser, userNTAccount, userGuid, trackingGuid,
            siteId, languageCulture, localeCulture);
        String contextInfoString = PSLibrary.PSContextInfo.SerializeToString( 
            contextInfo);
    
        return contextInfoString;
    }
    
  6. Ajoutez une méthode pour obtenir un GUID de ressource à partir d'un nom de compte et une méthode pour obtenir un nom de ressource à partir d'un GUID. Dans cet exemple, les méthodes GetResourceUid et GetResourceName sont dans une classe nommée ResourceUtilities. La classe est ajoutée au fichier Utilities.cs ; Vous pouvez également ajouter la classe dans un fichier distinct. Pour le code, consultez la section Utilities.cs dans l' Exemple de Code complet.

    La méthode GetResourceUid utilise un filtre pour réduire la quantité de données dans le ResourceDataSet retourné par l'appel à la méthode ReadResources. Pour plus d'informations sur l'utilisation de filtres dans les appels PSI, consultez How to: Use a Filter Parameter with PSI Methods.

Le fichier ImpersonationTest.cs doit les méthodes qui effectuent des tests pour l'emprunt d'identité. Dans cet exemple, la méthode NoImpersonation appelle GetCurrentUserUid() et affiche le GUID de l'utilisateur de l'application. La méthode NoImpersonation initialise également un objet ResourceUtilities, la méthode GetResourceUid avec l'utilisateur compte du fait que les appels seront dépersonnalisés, qui affiche dans la fenêtre de Console.

La méthode SimpleImpersonation appelle GetCurrentUserUid de nouveau, mais utilise le contexte de l'emprunt d'identité.

Procédure 4. Pour ajouter des tests de l'emprunt d'identité

  1. Ajoutez la méthode DisableFormsAuth qui définit le contexte d'opération Web pour activer l'authentification Windows dans une installation multi-authentication de Project Server.

    internal void DisableFormsAuth(bool isWindowsAuth)
    {
        WebOperationContext.Current.OutgoingRequest.Headers.Remove(
            "X-FORMS_BASED_AUTH_ACCEPTED");
    
        if (isWindowsAuth)
        {
            // Disable Forms authentication, to enable Windows authentication.
            WebOperationContext.Current.OutgoingRequest.Headers.Add(
                "X-FORMS_BASED_AUTH_ACCEPTED", "f");
        }
    }
    
  2. Ajoutez la méthode NoImpersonation à la classe ImpersonationTest dans le fichier ImpersonationTest.cs. La méthode obtient le GUID de l'utilisateur de l'application et le GUID de l'utilisateur pour emprunter l'identité. Les appels PSI sont effectuées en utilisant le contexte normal de l'utilisateur de l'application.

    Si le compte d'utilisateur n'existe pas dans Project Server, GetResourceUid renvoie un GUID vide (00000000-0000-0000-0000-000000000000) et la méthode NoImpersonation renvoie false. Si le compte n'est pas valide, l'application affiche un message d'erreur et n'utilise pas la méthode SimpleImpersonation.

    internal bool NoImpersonation()
    {
        ResourceUtilities resUtilities = new ResourceUtilities(resourceClient);
        bool isValidUser = true;
    
        // Limit the scope of WCF calls to the client channel. 
        using (OperationContextScope scope = new OperationContextScope(
            resourceClient.InnerChannel))
        {
            // Add a Web request header to enable Windows authentication in 
            // multi-authentication installations.
            DisableFormsAuth(isWindowsUser);
    
            // Get the GUID of the application user.
            appUserUid = resourceClient.GetCurrentUserUid();
            appUserAccount = resUtilities.GetResourceName(appUserUid);
    
            impersonatedUserUid = resUtilities.GetResourceUid(impersonatedUserAccount);
        }
    
        Console.WriteLine("GUID of {0}\n\tfrom GetCurrentUserUid:\t{1}", 
            appUserAccount, appUserUid);
        Console.WriteLine("\nGUID of user to impersonate,\n\tfrom ReadResources:\t{0}", 
            impersonatedUserUid.ToString());
    
        if (impersonatedUserUid == Guid.Empty)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Invalid account name for user to impersonate: {0}",
                impersonatedUserAccount);
            Console.ResetColor();
            isValidUser = false;
        }
        return isValidUser;
    }
    
  3. Ajoutez la méthode SimpleImpersonation à la classe ImpersonationTest. La méthode définit le contexte de l'emprunt d'identité, modifie l'en-tête de la demande Web sortant utiliser le contexte de l'emprunt d'identité et obtient ensuite le GUID de l'utilisateur représenté en utilisant la méthode GetCurrentUserUid. L'application limite la portée de l'emprunt d'identité avec un objet OperationContextScope. La propriété InnerChannel de l'objet resourceClient WCF est la demande Web qui contient l'en-tête « pjauth » avec le contexte de l'emprunt d'identité.

    Notes

    Bien que la description de OperationContextScope Class sur MSDN montre un exemple qui utilise la classe OperationContext pour modifier les en-têtes de message, vous devez utiliser WebOperationContext pour les services de Project Server.

    internal void SimpleImpersonation()
    {
        SetImpersonationContext(isWindowsUser, impersonatedUserAccount, impersonatedUserUid, 
            trackingGuid, siteId, languageCulture, localeCulture);
        bool pass = false;
    
        // Limit the scope of impersonation to the WCF client channel. 
        using (OperationContextScope scope = new OperationContextScope(
            resourceClient.InnerChannel))
        {
            // Use WebOperationContext in the HTTP channel, not the OperationContext class.
            WebOperationContext.Current.OutgoingRequest.Headers.Remove("PjAuth");
            WebOperationContext.Current.OutgoingRequest.Headers.Add("PjAuth", contextString);
    
            // Add a Web request header to enable Windows authentication in 
            // multi-authentication installations.
            DisableFormsAuth(isWindowsUser);
    
            // The impersonated user is now making the PSI calls.
            Guid getCurrentUserUidResult = resourceClient.GetCurrentUserUid();
    
            Console.WriteLine("GUID from GetCurrentUserUid:\t{0}", getCurrentUserUidResult);
            pass = getCurrentUserUidResult.Equals(impersonatedUserUid);
        }
    
        if (pass)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("\nThe expected GUID and actual GUID match.");
            Console.ResetColor();
        }
        else
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("\nFAIL! The expected and actual GUIDs do not match.");
            Console.ResetColor();
        }
    }
    

    Notes

    Si vous essayez d'utiliser la propriété System.Security.Principal.WindowsIdentity.GetCurrent().Name avant ou après la méthode SetImpersonationContext, la valeur dans les deux cas est le nom de l'utilisateur appelant du processus, pas l'utilisateur représenté. Pour les appels PSI dans une application basée sur WCF pour Project Server, l'emprunt d'identité est définie dans l'en-tête WebOperationContext, pas par un appel à la méthode WindowsIdentity.Impersonate.

  4. Ajoutez la méthode DisposeClients à la classe ImpersonationTest, pour fermer l'objet client WCF.

    internal void DisposeClients()
    {
        resourceClient.Close();
    }
    

La classe ImpersonationTest, qui est implémentée dans le fichier ImpersonationTest.cs et le fichier Utilities.cs, est terminée.

Si vous n'avez pas déjà fait, ajoutez la méthode RunTests et le reste de la méthode Main à la classe Program dans le fichier Program.cs. Ajoutez le fichier app.config, modifiez l'adresse de point de terminaison dans l'élément client pour correspondre à l'adresse ProjectServer.svc sur votre ordinateur, puis compilez et exécutez l'application. La figure 4 illustre la fenêtre d'invite de commandes Visual Studio où WCFImpersonationTest est exécuté trois fois, comme suit :

  1. WCFImpersonationTest : affiche le message d'utilisation.

  2. WCFImpersonationTest –pwaUrl https://localhost/pwa -config : utilise app.config pour définir des points de terminaison client. Le compte d'emprunt d'identité est un utilisateur de Project Server valide.

  3. WCFImpersonationTest –pwaUrl https://localhost/pwa : définit le client points de terminaison par programme. Le compte d'emprunt d'identité n'existe pas sur Project Server.

La figure 4. Sortie de l'application WCFImpersonationTest

Sortie de l’application WCFImpersonationTest

Exemple de Code complet

Fichier Program.cs   Le code suivant est le contenu complet de la classe Program dans l'application WCFImpersonationTest.

using System;
using System.Text;
using System.ServiceModel;

namespace WCFImpersonationTest
{
    class Program
    {
        static private Uri pwaUri;          // URI of Project WebApp.
        static private string pwaUrl;       // URL of Project WebApp.
        static private bool useConfig;      // If true, use app.config for client endpoints.
                                            // If false, programmatically configure endpoints.
        static private string argError;
        static private ConsoleColor foreColor;
        static private ImpersonationTest impersonationTest;

        static void Main(string[] args)
        {
            pwaUrl = string.Empty;
            pwaUri = null;
            useConfig = false;
            argError = null;

            foreColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Gray;

            if (args.Length == 0)
            {
                Usage(argError);
            }
            else if (ParseCommandLine(args))
            {
                if (useConfig)
                    Console.WriteLine("Using app.config to set client endpoints.");
                else
                    Console.WriteLine("Programmatically setting client endpoints.");

                Console.WriteLine(@"Enter a valid Project Server user account, such as domain\username.");
                Console.Write("\nAccount for impersonation:\t");
                Console.ForegroundColor = ConsoleColor.Cyan;

                string userAccount = Console.ReadLine();
                Console.WriteLine();
                Console.ResetColor();

                impersonationTest = new ImpersonationTest(pwaUri, userAccount, useConfig);

                RunTests();

                // Close the client.
                impersonationTest. DisposeClients();
            }
            else
            {
                Usage(argError);
            }
            ExitApp();
        }

        static private void RunTests()
        {
            bool isValidUser;

            try
            {
                // Test 1 - Use Resource service without impersonation.
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Test 1: No impersonation");
                Console.ResetColor();

                isValidUser = impersonationTest. NoImpersonation();

                // Test 2 - Use simple impersonation.
                if (isValidUser)
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("\nTest 2: Simple impersonation");
                    Console.ResetColor();

                    impersonationTest. SimpleImpersonation();
                }
            }
            catch (EndpointNotFoundException e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("\n***EndpointNotFoundException:");
                Console.WriteLine("   Check the app.config file or the programmatic"
                    + "\n   configuration for the correct endpoint address,"
                    + "\n   and the binding, endpoint, and contract names.\n");
                Console.WriteLine(e.ToString());
                Console.ResetColor();
            }
            catch (CommunicationException e)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("\n***CommunicationException:\n");
                Console.WriteLine(e.ToString());
                Console.ResetColor();
            }
        }

        static private void ExitApp()
        {
            Console.Write("\nPress any key to exit: ");
            Console.ReadKey(true);
            Console.ForegroundColor = foreColor;
        }

        static private bool ParseCommandLine(string[] args)
        {
            bool error = false;
            bool argErr = false;

            int argsLength = args.Length;

            for (int i = 0; i < args.Length; ++i)
            {
                if (error) break;

                switch (args[i].ToLower())
                {
                    case "/pwaurl":
                    case "-pwaurl":
                        i++;
                        if (i >= argsLength) return false;
                        pwaUrl = args[i];
                        pwaUrl = pwaUrl.ToLower();

                        if (pwaUrl.StartsWith("http") || pwaUrl.StartsWith("https"))
                        {
                            // Add a trailing slash, if it is not present.
                            if (pwaUrl.LastIndexOf("/") != pwaUrl.Length - 1)
                                pwaUrl += "/";

                            // Convert to a URI, for easy access to properties.
                            pwaUri = new Uri(pwaUrl);
                        }
                        else
                            argErr = true;
                        break;
                    case "/config":
                    case "-config":
                        useConfig = true;
                        break;

                    case "/?":
                    case "-?":
                        error = true;
                        break;

                    default:
                        argError = args[i];
                        error = true;
                        break;
                }
            }
            if (pwaUrl == string.Empty) error = true;
            if (argErr) error = true;

            return !error;
        }

        static private void Usage(String errorInfo)
        {
            if (errorInfo != null)
            {
                // A command-line argument error occurred, report it to the user.
                Console.WriteLine("Error: {0} is not a valid argument.\n", errorInfo);
            }

            Console.WriteLine(string.Format(
                "Usage:  [-?] -{0} {1} [-{2}]",
                "pwaUrl", @"<URL>", "config"));

            Console.WriteLine(
                "\n\tpwaUrl:\t\t\tExample: https://ServerName/pwa"
                + "\n\tConfig (optional):\tUse app.config to set endpoints");
        }
    }
}

Fichier de ImpersonationTest.cs   Le code suivant est contenu partiel pour la classe ImpersonationTest.

using System;
using System.ServiceModel;
using System.Globalization;
using System.ServiceModel.Web;
using PSLibrary = Microsoft.Office.Project.Server.Library;
using SvcResource;

namespace WCFImpersonationTest
{
    public partial class ImpersonationTest
    {
        private static ResourceClient resourceClient;   // Client of the SvcResource reference.
        private static string appUserAccount;           // Name of the application user.
        private static Guid appUserUid;                 // GUID of the application user.
        private static string impersonatedUserAccount;  // User to impersonate.
        private static Guid impersonatedUserUid;        // GUID of user to impersonate.
        private static String contextString = String.Empty; // Impersonation context.

        // Variables for setting the impersonation context.
        bool isWindowsUser = true;          // Use a Windows account in this test application.
        Guid trackingGuid = Guid.Empty;     // The tracking GUID for Queue operations is not used.
        Guid siteId = Guid.Empty;           // The Project Web Appsite ID is not used.
        CultureInfo languageCulture = null; // The language culture is not used.
        CultureInfo localeCulture = null;   // The locale culture is not used.

        // ImpersonationTest constructor. Set the user account and the client endpoints. 
        public ImpersonationTest(Uri uri, string user, bool useConfig)
        {
            impersonatedUserAccount = user;

            if (useConfig)
                ConfigClientEndpoints();
            else
                SetClientEndpoints(uri);
        }

        // Close the WCF client object.
         internal void DisposeClients()
        {
            resourceClient.Close();
        }

        // Get the GUIDs of the application user and the user to impersonate,  
        // without using impersonation.
        internal bool NoImpersonation()
        {
            ResourceUtilities resUtilities = new ResourceUtilities(resourceClient);
            bool isValidUser = true;

            // Limit the scope of WCF calls to the client channel. 
            using (OperationContextScope scope = new OperationContextScope(
                resourceClient.InnerChannel))
            {
                // Add a Web request header to enable Windows authentication
                // in multi-authentication installations.
                DisableFormsAuth(isWindowsUser);

                // Get the GUID of the application user.
                appUserUid = resourceClient.GetCurrentUserUid();
                appUserAccount = resUtilities.GetResourceName(appUserUid);

                impersonatedUserUid = resUtilities.GetResourceUid(impersonatedUserAccount);
            }

            Console.WriteLine("GUID of {0}\n\tfrom GetCurrentUserUid:\t{1}", 
                appUserAccount, appUserUid);
            Console.WriteLine("\nGUID of user to impersonate,\n\tfrom ReadResources:\t{0}", 
                impersonatedUserUid.ToString());

            if (impersonatedUserUid == Guid.Empty)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Invalid account name for user to impersonate: {0}",
                    impersonatedUserAccount);
                Console.ResetColor();
                isValidUser = false;
            }
            return isValidUser;
        }

        // Get the GUID of the impersonated user, by using the GetCurrentUserUid method.
        internal void SimpleImpersonation()
        {
            SetImpersonationContext(isWindowsUser, impersonatedUserAccount, impersonatedUserUid, 
                trackingGuid, siteId, languageCulture, localeCulture);
            bool pass = false;

            // Limit the scope of impersonation to the WCF client channel. 
            using (OperationContextScope scope = new OperationContextScope(
                resourceClient.InnerChannel))
            {
                // Use WebOperationContext in the HTTP channel, not the OperationContext class.
                WebOperationContext.Current.OutgoingRequest.Headers.Remove("PjAuth");
                WebOperationContext.Current.OutgoingRequest.Headers.Add("PjAuth", contextString);
                
                // Add a Web request header to enable Windows authentication
                // in multi-authentication installations.
                DisableFormsAuth(isWindowsUser);
                
                // The impersonated user is now making the PSI calls.
                Guid getCurrentUserUidResult = resourceClient.GetCurrentUserUid();

                Console.WriteLine("GUID from GetCurrentUserUid:\t{0}", getCurrentUserUidResult);
                pass = getCurrentUserUidResult.Equals(impersonatedUserUid);
            }

            if (pass)
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("\nThe expected GUID and actual GUID match.");
                Console.ResetColor();
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("\nFAIL! The expected and actual GUIDs do not match.");
                Console.ResetColor();
            }
        }

        // For Windows authentication in a multi-authentication installation, 
        // the Web request must disable Forms authentication. 
        // Call DisableFormsAuth within an OperationContextScope.
        internal void DisableFormsAuth(bool isWindowsAuth)
        {
            WebOperationContext.Current.OutgoingRequest.Headers.Remove(
                "X-FORMS_BASED_AUTH_ACCEPTED");

            if (isWindowsAuth)
            {
                // Disable Forms authentication, to enable Windows authentication.
                WebOperationContext.Current.OutgoingRequest.Headers.Add(
                    "X-FORMS_BASED_AUTH_ACCEPTED", "f");
            }
        }

    }
}

Fichier de Utilities.cs   Le code suivant inclut du contenu partiel pour les classes ImpersonationTest et ResourceUtilities complète.

using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.ServiceModel;
using System.Globalization;
using PSLibrary = Microsoft.Office.Project.Server.Library;
using SvcResource;

namespace WCFImpersonationTest
{
    // Contains methods to set the client endpoints and set the impersonation context.
    public partial class ImpersonationTest
    {
        // Add validation code for callback if using X509 certificate and HTTPS protocol. 
        private static bool CustomCertificateValidation(
            object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
        {
            return true;
        }

        // Use the endpoints defined in app.config to configure the client.
        public static void ConfigClientEndpoints()
        {
            resourceClient = new ResourceClient(
                "basicHttp_Resource");
        }

        // Programmatically set the endpoint of the Resource service client.
        public static void SetClientEndpoints(Uri pwaUri)
        {
            // Set the address of the front-end WCF router.
            const string svcRouter = "_vti_bin/PSI/ProjectServer.svc";

            string pwaUrl = pwaUri.Scheme + Uri.SchemeDelimiter + pwaUri.Host + ":"
                + pwaUri.Port + pwaUri.AbsolutePath;

            BasicHttpBinding binding = null;

            if (pwaUri.Scheme.Equals(Uri.UriSchemeHttps))
            {
                ServicePointManager.ServerCertificateValidationCallback +=
                    new RemoteCertificateValidationCallback( 
                        CustomCertificateValidation);

                binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
            }
            else
            {
                binding = new BasicHttpBinding(
                    BasicHttpSecurityMode.TransportCredentialOnly);
            }

            binding.Name = "basicHttpConf";
            binding.SendTimeout = TimeSpan.MaxValue;
            ((BasicHttpBinding)binding).MaxReceivedMessageSize = 500000000;
            ((BasicHttpBinding)binding).ReaderQuotas.MaxNameTableCharCount = 500000000;
            ((BasicHttpBinding)binding).MessageEncoding = WSMessageEncoding.Text;
            ((BasicHttpBinding)binding).Security.Transport.ClientCredentialType =
                HttpClientCredentialType.Ntlm;

            EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter);

            resourceClient = new ResourceClient(binding, address);
            resourceClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel =
                TokenImpersonationLevel.Impersonation;
            resourceClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
        }

        // Set the impersonation context for calls to the PSI on behalf of 
        // the impersonated user.
        internal static void SetImpersonationContext(
                                bool isWindowsUser, String userNTAccount,
            Guid userGuid, Guid trackingGuid, Guid siteId,
            CultureInfo languageCulture, CultureInfo localeCulture)
        {
            contextString = GetImpersonationContext(
                                isWindowsUser, userNTAccount, userGuid,
                trackingGuid, siteId, languageCulture, localeCulture);
        }

        // Get the impersonation context.
        private static String GetImpersonationContext(
            bool isWindowsUser, String userNTAccount,
            Guid userGuid, Guid trackingGuid, Guid siteId,
            CultureInfo languageCulture, CultureInfo localeCulture)
        {
            PSLibrary.PSContextInfo contextInfo = new PSLibrary.PSContextInfo(
                isWindowsUser, userNTAccount, userGuid, trackingGuid,
                siteId, languageCulture, localeCulture);
            String contextInfoString = PSLibrary.PSContextInfo.SerializeToString( 
                contextInfo);

            return contextInfoString;
        }
    }

    // Utilities for using the Resource service.
    public class ResourceUtilities
    {
        private ResourceClient resourceClient;

        public ResourceUtilities(ResourceClient resClient)
        {
            resourceClient = resClient;
        }

        // Get the GUID for a Project Server account name. 
        public Guid GetResourceUid(String accountName)
        {
            Guid resourceUid = Guid.Empty;
            ResourceDataSet resourceDs = new ResourceDataSet();

            // Filter for the account name, which can be a 
            // Windows account or Project Server account.
            PSLibrary.Filter filter = new PSLibrary.Filter();
            filter.FilterTableName = resourceDs.Resources.TableName;

            PSLibrary.Filter.Field accountField = new PSLibrary.Filter.Field(
                    resourceDs.Resources.TableName,
                    resourceDs.Resources.WRES_ACCOUNTColumn.ColumnName);
            filter.Fields.Add(accountField);

            PSLibrary.Filter.FieldOperator op = new PSLibrary.Filter.FieldOperator(
                    PSLibrary.Filter.FieldOperationType.Equal,
                    resourceDs.Resources.WRES_ACCOUNTColumn.ColumnName, accountName);
            filter.Criteria = op;

            string filterXml = filter.GetXml();

            resourceDs = resourceClient.ReadResources(filterXml, false);

            // Return the account GUID.
            if (resourceDs.Resources.Rows.Count > 0)
                resourceUid = (Guid)resourceDs.Resources.Rows[0]["RES_UID"];

            return resourceUid;
        }

        // Get the account name from the user GUID. 
        public string GetResourceName(Guid accountUid)
        {
            string accountName = "[account unknown]";
            ResourceDataSet resourceDs = new ResourceDataSet();

            resourceDs = resourceClient.ReadResource(accountUid);

            if (resourceDs.Resources.Rows.Count > 0)
                accountName = (string)resourceDs.Resources.Rows[0]["WRES_ACCOUNT"];

            return accountName.ToLower();
        }
    }
}

Fichier app.config   Le code suivant est le fichier de configuration de l'application. Il n'est pas utilisée lorsque l'application définit par programme les liaisons WCF et les points de terminaison de l'objet resourceClient. Modifier l'adresse de point de terminaison dans l'élément client pour correspondre à l'adresse ProjectServer.svc sur votre ordinateur. Pour plus d'informations sur les liaisons WCF de paramètre et les points de terminaison dans un fichier app.config, consultez Procédure pas à pas : Développement d’applications PSI à l’aide de WCF (traduction automatique).

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <behaviors>
            <endpointBehaviors>
                <behavior name="basicHttpBehavior">
                    <clientCredentials>
                        <windows allowedImpersonationLevel="Impersonation" />
                    </clientCredentials>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <bindings>
            <basicHttpBinding>
                <binding name="basicHttpConf" sendTimeout="01:00:00" maxBufferSize="500000000"
                    maxReceivedMessageSize="500000000">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="500000000" />
                    <security mode="TransportCredentialOnly">
                        <transport clientCredentialType="Ntlm" realm="http"/>
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://ServerName/ProjectServerName/_vti_bin/PSI/ProjectServer.svc"
                behaviorConfiguration="basicHttpBehavior" binding="basicHttpBinding"
                bindingConfiguration="basicHttpConf" contract="SvcResource.Resource"
                name="basicHttp_Resource" />
        </client>
    </system.serviceModel>
</configuration>

Notes

Avertissement traduction automatique : cet article a été traduit par un ordinateur, sans intervention humaine. Microsoft propose cette traduction automatique pour offrir aux personnes ne maîtrisant pas l’anglais l’accès au contenu relatif aux produits, services et technologies Microsoft. Comme cet article a été traduit automatiquement, il risque de contenir des erreurs de grammaire, de syntaxe ou de terminologie.

Voir aussi

Tâches

Procédure pas à pas : Développement d’applications PSI à l’aide de WCF (traduction automatique)

Procédure : Créer un gestionnaire d’événements Project Server et consigner un événement (traduction automatique)

Concepts

Conditions préalables pour les exemples de code basés sur WCF (traduction automatique)

Autres ressources

Using Impersonation in Project Server

How to: Use a Filter Parameter with PSI Methods

OperationContextScope Class