Comment convertir WindowsIdentity en un NetworkCredential?

Comment convertir une WindowsIdentity en un NetworkCredential ? Je teste mon service WCF pour vérifier que les appelants anonymes sont bloqués. Pour ce faire, je veux faire quelque chose comme:

 myProxy.ClientCredentials.Windows.ClientCredential = foo(WindowsIdentity.GetAnonymous()); 

foo est une méthode qui convertit une WindowsIdentity en un NetworkCredential

De manière générale, foo n’existe pas. Depuis un NetworkCredential est un object plus large que WindowsIdentity. C’est-à-dire que je peux utiliser NetworkCredential où j’ai besoin d’une WindowsIdentity mais pas l’inverse.

La raison est due à la sécurité.

NetworkCredential signifie que vous êtes autorisé à utiliser cette identité et à utiliser ses CREDENTIALS associés sur une autre machine.

Cela signifie que

  1. Vous avez les informations d’identification des utilisateurs, par opposition à leur identité.
  2. Cet ensemble de références est bon pour l’emprunt d’identité, pas seulement pour l’access local.

Je suppose que les informations d’identification proviennent d’une autre machine (en raison de WCF). Toujours pour des raisons de sécurité, cette référence réseau a été convertie en une référence LocalCredential lors du passage sur cette machine (sauf s’il est question de délégation plutôt que d’emprunt d’emprunt). Le poste client A DONNÉ le droit d’utiliser ses informations d’identification sur le serveur et uniquement sur le serveur.

Si vous souhaitez récupérer NetworkCredential, vous devez effectuer une opération appelée Délégation, en raison de ce que l’on appelle le problème du multi-saut. Cela concerne Kerberos, un chien maléfique à trois têtes des enfers. Vous ne voulez pas faire face à Kerberos.

De manière générale, par défaut, les mandataires WCF n’envoient pas d’informations d’identification avec leurs appels. Vous devez généralement définir les identifiants ClientCredentials ou

 proxy.UseDefaultCredentials = true 

Ne pas fournir non signifie normalement pas d’informations d’identification, d’où l’authentification anonyme.

Répondant à ma propre question:
Il n’est pas possible de convertir une WindowsIdentity en un NetworkCredential . Pour vérifier si des appelants anonymes sont bloqués, empruntez l’identité du thread actuel avec un jeton de session null , puis appelez le service WCF. Remarque: n’utilisez pas WindowsIdentity.GetAnonymous . Cette méthode est inutile (supposez qu’elle a été implémentée de manière incorrecte et qu’elle n’a jamais été corrigée). Code pour emprunter l’identité du thread actuel avec un jeton de session nul (aucune gestion d’erreur n’est effectuée):

  public static class Helper { [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] private static extern IntPtr GetCurrentThread(); [DllImport("advapi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] private static extern bool ImpersonateAnonymousToken(IntPtr handle); public static void ImpersonateAnonymousUser() { ImpersonateAnonymousToken(GetCurrentThread()); } } static ssortingng ToSsortingng(IIdentity identity) { return ssortingng.Format("{0} {1} {2}", identity.Name, identity.IsAuthenticated, identity.AuthenticationType); } static void Main(ssortingng[] args) { Console.WriteLine(ToSsortingng(WindowsIdentity.GetCurrent())); Helper.ImpersonateAnonymousUser(); Console.WriteLine(ToSsortingng(WindowsIdentity.GetCurrent())); } 

Sortie:

 my machine\me True NTLM NT AUTHORITY\ANONYMOUS LOGON False 

En réponse au commentaire d’Edmund, définir proxy.ClientCredentials.Windows.ClientCredential sur null ne fera pas ce qui est indenté – faire une demande en tant qu’utilisateur anonyme. Voici mon code de test complet et sa sortie:

Code de service:

 public class Service1 : IService1 { // note that if client is not authenticated, this code will never get a chance to execute // exception will happen before that // therefore there is no need to decorate this method with a // [PrincipalPermission(SecurityAction.Demand, Authenticated=true)] atsortingbute public ssortingng GetData() { try { var identity = Thread.CurrentPrincipal.Identity; return ssortingng.Concat(identity.Name, ",", identity.IsAuthenticated, ",", identity.AuthenticationType); } catch (Exception e) { return ssortingng.Concat(e.Message, "\\r\\n", e.StackTrace); } } } 

Service config:

                     

Code client:

 static void MakeRequest() { try { using (var svc = new Service1Client()) { Console.WriteLine(svc.GetData()); } } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); } } static void Test3() { Console.WriteLine("using {0}", ToSsortingng(WindowsIdentity.GetCurrent())); MakeRequest(); Console.WriteLine(); Console.WriteLine("setting svc.ClientCredentials.Windows.ClientCredential to null..."); try { using (var svc = new Service1Client()) { svc.ClientCredentials.Windows.ClientCredential = null; Console.WriteLine(svc.GetData()); } } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); } Console.WriteLine(); ImpersonateAnonymousUser(); Console.WriteLine("using {0}", ToSsortingng(WindowsIdentity.GetCurrent())); MakeRequest(); Console.WriteLine(); } 

Configuration client:

                           

Sortie:

 using mymachine\me True Negotiate mymachine\me,True,Negotiate setting svc.ClientCredentials.Windows.ClientCredential to null... mymachine\me,True,Negotiate using NT AUTHORITY\ANONYMOUS LOGON False The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state. Server stack trace: at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req Msg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa ta, Int32 type) at System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout) at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject. Close(TimeSpan timeout) at System.ServiceModel.ClientBase`1.Close() at System.ServiceModel.ClientBase`1.System.IDisposable.Dispose() at TestClient.Program.MakeRequest() 

Nous avons une série de tests que nous faisons et nous utilisons cette méthode d’assistance:

 public static ResponseType CallService(RequestType requestBody, NetworkCredential credential) { ResponseType response; using (var channelFactory = new ChannelFactory("BasicHttpBinding_IMyService")) { channelFactory.Credentials.Windows.ClientCredential = credential; var client = channelFactory.CreateChannel(); var request = new MyRequest() { MyService = requestBody }; response = client.MyService(request).MyResponse1; ((IClientChannel)client).Close(); channelFactory.Close(); } return response; } 

utilisation dans les tests:

 var requestBody = GetRequestBody();//another helper method var credential = new NetworkCredential { Domain = "MyDomain", UserName = "MyUsername", Password = "MyPassword" }; var response = CallService(requestBody, credential); //Assert various user credentials var response = CallService(requestBody, null); //Assert anonymous