Workflow First and Security, exemple
Cet exemple illustre le fonctionnement de deux fonctionnalités critiques des services de worflow :
Méthode de création des services « workflow en premier »
Les workflows vous permettent de créer des services de deux manières différentes. La première correspond à la méthode « contrat en premier » dans laquelle le contrat existe déjà et au cours de laquelle le workflow implémente le contrat. La seconde correspond à la méthode « workflow en premier » au cours de laquelle le contrat est généré lors de la création du workflow. L'action d'un concepteur de workflow s'apparente en fait à celle d'un concepteur de contrats. Dans cet exemple, le contrat de calculatrice est généré lorsque le workflow est créé.
Dans cet exemple, la balise correspondant à la méthode « workflow en premier » est implémentée dans le fichier SequentialCalculatorService.xoml. Pour comprendre le modèle de programmation qui sous-tend la programmation « workflow en premier », comparez cette balise à celle des autres exemples de workflow.
L'exemple de code suivant contient la balise correspondant à l'activité Receive.<ns0:ReceiveActivity x:Name="ReceiveAdd" OperationValidation="ValidateOwner" CanCreateInstance="True"> <ns0:ReceiveActivity.ServiceOperationInfo> <ns0:OperationInfo PrincipalPermissionRole="Administrators" Name="Add" ContractName="ICalculator"> <ns0:OperationInfo.Parameters> <ns0:OperationParameterInfo Attributes="In" ParameterType="{x:Type p15:Int32}" Name="value" Position="0" xmlns:p15="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <ns0:OperationParameterInfo Attributes="Out" ParameterType="{x:Type p15:Int32}" Name="returnValue" Position="1" xmlns:p15="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </ns0:OperationInfo.Parameters> </ns0:OperationInfo> </ns0:ReceiveActivity.ServiceOperationInfo> <ns0:ReceiveActivity.ParameterBindings> <WorkflowParameterBinding ParameterName="value"> <WorkflowParameterBinding.Value> <ActivityBind Name="SequentialCalculatorService" Path="inputValue" /> </WorkflowParameterBinding.Value> </WorkflowParameterBinding> <WorkflowParameterBinding ParameterName="returnValue"> <WorkflowParameterBinding.Value> <ActivityBind Name="SequentialCalculatorService" Path="currentValue" /> </WorkflowParameterBinding.Value> </WorkflowParameterBinding> </ns0:ReceiveActivity.ParameterBindings> <CodeActivity x:Name="DoAdd" ExecuteCode="Add" /> </ns0:ReceiveActivity>
L'activité Receive ne contient pas de référence renvoyant à un type de contrat externe. Le contrat est défini dans l'activité Receive elle-même. Le bloc
OperationInfo
de l'activité Receive contient à la fois le nom du contrat et celui de l'opération. Il s'agit de chaînes littérales ne contenant pas de références au type définissant le contrat.
Lorsque vous exécutez l'exemple, le service de workflow utilise cette définition afin de générer la description du service. Pour créer un client de service pour ce service, utilisez l'outil ServiceModel Metadata Utility Tool (Svcutil.exe).Sécurité au niveau des services de workflow
Les services de workflow vous permettent de choisir entre deux niveaux de sécurité pour votre service. Si vous utilisez le premier niveau de sécurité, vous devez spécifier l'autorisation de base accordée aux opérations. L'exécution du service contrôle le niveau d'autorisation avant de remettre le message au workflow. Si le message ne respecte pas les critères définis par cette autorisation de base, il n'est pas remis au workflow. Le deuxième niveau de sécurité correspond à la condition de validation des opérations, condition que vous pouvez assigner à l'activité Receive. Cette condition de validation est exécutée lorsque l'activité Receive reçoit le message. Si le message ne respecte pas cette condition, une erreur est envoyée au client, le message est supprimé et le workflow retourne une défaillance.
L'attribut PrincipalPermissionRole de l'élément OperationInfo indique que seuls les utilisateurs du groupe Administrateurs sont autorisés à appeler cette opération.
L'attribut OperationValidation de l'activité Receive renvoie au gestionnaire de code à exécuter comme condition de validation. Dans cet exemple, seuls les utilisateurs du groupe Administrateurs peuvent appeler la première opération, l'attribut PrincipalPermissionRole étant défini sur la première activité Receive. Dans ce workflow, l'activité ReceiveReceiveAdd
fait affecter à la propriété CanCreateInstance la valeur true. Cela signifie qu'une instance du service est créée lorsqu'un utilisateur du groupe Administrateurs appelle une opération d'addition.
Au cours des opérations de réception suivantes, la condition OperationValidation s'assure que l'entité qui appelle l'opération est la même que celle qui a créé l'instance. Dans ce scénario, seule la personne ayant initié l'instance peut envoyer d'autres messages. L'exemple de code suivant contient la condition OperationValidation procédant à cette vérification.private void ValidateOwner(object sender, OperationValidationEventArgs e) { if (string.IsNullOrEmpty(owner)) { owner = ExtractCallerName(e.ClaimSets); e.IsValid = true; Console.WriteLine("Owner: " + owner); } if (owner.Equals(ExtractCallerName(e.ClaimSets))) e.IsValid = true; } private string ExtractCallerName(ReadOnlyCollection<ClaimSet> claimSets) { string owner = string.Empty; foreach (ClaimSet claims in claimSets) { foreach (Claim claim in claims) { if (claim.ClaimType.Equals(ClaimTypes.Name) && claim.Right.Equals(Rights.PossessProperty)) { owner = claim.Resource.ToString(); break; } } } return owner; }
La méthode
ValidateOwner
indiquée fournit des ensembles de revendications sous forme d'argumentOperationValidationEventArgs
. À l'aide de ces ensembles, elle clôt le processus de validation des messages. Remarque : à ce stade, les paramètres effectifs du corps des messages ne sont pas encore disponibles. La méthodeExtractCallerName
extrait le nom de l'appelant de la revendication de nom, puis stocke celui-ci. Au cours des demandes suivantes, le nom de l'appelant est comparé à la revendication de nom figurant dans les messages entrants afin de s'assurer que la personne qui envoie ces messages et bien celle qui a envoyé le premier message (et qui donc a généré l'instance).
Remarque : |
---|
Cet exemple requiert l'installation de .NET Framework version 3.5 pour être généré et exécuté. Visual Studio 2008 est nécessaire pour l'ouverture des fichiers projet et solution. |
Pour configurer, générer et exécuter le projet
Conformez-vous aux instructions d'installation figurant dans One-time Setup Procedure for Windows Communication Foundation Samples.
Exécutez le script CreateStores.cmd figurant dans la rubrique One-Time Setup Procedure for the Windows Communication Foundation Samples.
Générez la solution.
Exécutez l'exemple en tant qu'utilisateur du groupe Administrateurs. Lancez d'abord le fichier exécutable du service, puis celui du client. Si vous utilisez Windows Vista, cliquez avec le bouton droit de la souris sur le fichier exécutable, puis cliquez sur Exécuter en tant qu'administrateur. L'exemple s'exécute entièrement.
Essayez d'exécuter l'exemple sous une autre identité d'utilisateur. Utilisez la commande Exécuter en tant que... à cette fin. Lorsque l'identité n'appartient pas au groupe Administrateurs, le service retourne une erreur.
Exécution de l'exemple sur des ordinateurs distincts
Modifiez les fichiers de configuration du service et du client en n'oubliant de changer le nom du serveur figurant dans l'adresse du point de terminaison. Remplacez la valeur
localhost
utilisée comme nom de serveur par le nom de l'ordinateur sur lequel le service est exécuté.Le service utilisant le port 8888, vous devez ouvrir ce port dans le pare-feu. Sélectionnez le panneau de configuration de Windows, puis cliquez sur Pare-feu Windows. Cliquez sur Ajouter un port, puis ajoutez le port 8888. Vous pouvez également exécuter le service sur le port par défaut. Pour ce faire, supprimez :8888 de l'adresse du point de terminaison.
Modifiez le fichier de configuration du client, puis ajoutez l'élément suivant comme enfant de l'élément
<endpoint>
.<identity> <UserPrincipalName value=”*@<Domain Name in which your server is running” /> </identity>
Pour déterminer le nom de domaine, démarrez le service depuis l'ordinateur sur lequel vous projetez de l'exécuter. Dans une fenêtre d'invite de commandes de l'autre ordinateur, exécutez la commande suivante.
svcutil.exe http://<serverName>:8888/servicehost/Calculator.svc
Cette commande génère un fichier .cs ainsi qu'un fichier output.config. Dans l'élément
<endpoint>
du fichier de configuration du client copiez l'élément<identity>
.