Comment fournir un nom d'utilisateur et un mot de passe lors de la connexion à un partage réseau


191

Lors de la connexion à un partage réseau pour lequel l'utilisateur actuel (dans mon cas, un utilisateur de service activé par le réseau) n'a aucun droit, le nom et le mot de passe doivent être fournis.

Je sais comment faire cela avec les fonctions Win32 (la WNet*famille de mpr.dll), mais j'aimerais le faire avec la fonctionnalité .Net (2.0).

Quelles options sont disponibles?

Peut-être que quelques informations supplémentaires peuvent vous aider:

  • Le cas d'utilisation est un service Windows, pas une application Asp.Net.
  • Le service s'exécute sous un compte qui n'a aucun droit sur le partage.
  • Le compte utilisateur requis pour le partage n'est pas connu côté client.
  • Le client et le serveur ne sont pas membres du même domaine.

7
Bien que je ne vous donne pas de réponse utile, je peux fournir une anti-réponse. L'usurpation d'identité et la création d'un processus comme Marc l'a suggéré ne fonctionneront pas lorsque le serveur et le client ne sont pas dans le même domaine, à moins qu'il n'y ait une confiance entre les deux domaines. S'il y a une confiance, je pense que cela fonctionnera. J'aurais juste répondu en commentaire à Marc mais je n'ai pas assez de représentant pour commenter. : - /
Moose

Réponses:


152

Vous pouvez modifier l'identité du thread ou P / Invoke WNetAddConnection2. Je préfère ce dernier, car j'ai parfois besoin de conserver plusieurs informations d'identification pour différents endroits. Je l'enveloppe dans un IDisposable et j'appelle WNetCancelConnection2 pour supprimer les creds par la suite (en évitant l'erreur de plusieurs noms d'utilisateur):

using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
   File.Copy(@"\\server\read\file", @"\\server2\write\file");
}

4
Le service n'est pas membre du domaine cible - l'emprunt d'identité ne peut pas fonctionner car vous ne pourriez pas créer le jeton de sécurité localement et emprunter l'identité avec lui. PInvoke est le seul moyen.
stephbu

@MarkBrackett Je sais que c'est une vieille réponse, mais peut-être que vous savez toujours ... l'accès sera-t-il accordé uniquement au programme ou également à l'utilisateur connecté via l'explorateur?
Breeze

@Breeze - Je ne l'ai pas testé, mais je m'attendrais à ce qu'il s'authentifie pour la session d'ouverture de session; donc si votre programme s'exécute en tant qu'utilisateur connecté, il y aura également accès (au moins pour la durée de l'opération).
Mark Brackett

8
Les définitions de readCredentials et writeCredentials pourraient être incluses dans la réponse.
Anders Lindén

2
Si vous obtenez l' erreur 53 , assurez-vous que le chemin ne se termine pas par un "\"
Mustafa S.

327

J'ai tellement aimé la réponse de Mark Brackett que j'ai fait ma propre mise en œuvre rapide. Le voici si quelqu'un d'autre en a besoin rapidement:

public class NetworkConnection : IDisposable
{
    string _networkName;

    public NetworkConnection(string networkName, 
        NetworkCredential credentials)
    {
        _networkName = networkName;

        var netResource = new NetResource()
        {
            Scope = ResourceScope.GlobalNetwork,
            ResourceType = ResourceType.Disk,
            DisplayType = ResourceDisplaytype.Share,
            RemoteName = networkName
        };

        var userName = string.IsNullOrEmpty(credentials.Domain)
            ? credentials.UserName
            : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);

        var result = WNetAddConnection2(
            netResource, 
            credentials.Password,
            userName,
            0);

        if (result != 0)
        {
            throw new Win32Exception(result);
        }   
    }

    ~NetworkConnection()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        WNetCancelConnection2(_networkName, 0, true);
    }

    [DllImport("mpr.dll")]
    private static extern int WNetAddConnection2(NetResource netResource, 
        string password, string username, int flags);

    [DllImport("mpr.dll")]
    private static extern int WNetCancelConnection2(string name, int flags,
        bool force);
}

[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
    public ResourceScope Scope;
    public ResourceType ResourceType;
    public ResourceDisplaytype DisplayType;
    public int Usage;
    public string LocalName;
    public string RemoteName;
    public string Comment;
    public string Provider;
}

public enum ResourceScope : int
{
    Connected = 1,
    GlobalNetwork,
    Remembered,
    Recent,
    Context
};

public enum ResourceType : int
{
    Any = 0,
    Disk = 1,
    Print = 2,
    Reserved = 8,
}

public enum ResourceDisplaytype : int
{
    Generic = 0x0,
    Domain = 0x01,
    Server = 0x02,
    Share = 0x03,
    File = 0x04,
    Group = 0x05,
    Network = 0x06,
    Root = 0x07,
    Shareadmin = 0x08,
    Directory = 0x09,
    Tree = 0x0a,
    Ndscontainer = 0x0b
}

10
Cela devrait vraiment être throw new Win32Exception(result);, puisque WNetAddConnection2 renvoie les codes d'erreur win32 ( ERROR_XXX)
torvin

2
C'est un petit bout de code brillant. Nécessaire pour se connecter à un système UNIX pour obtenir une liste de répertoires pour l'impression sur une application Web MVC5 et cela a fait l'affaire. +1 !!!
Tay

3
Les instructions using suivantes sont requises pour que le code ci-dessus soit compilé: using System.Net; using System.Runtime.InteropServices; using System.ComponentModel;
Matt Nelson du

4
désolé d'actualiser cet ancien thread, mais il semble qu'il ne ferme pas la connexion une fois le blocage terminé. J'ai un programme pour télécharger quelques photos, le premier fonctionne bien, le second échoue. La connexion est libérée lorsque le programme est fermé. Un conseil?
arti

3
Nous avons eu le même problème que vous, @arti. En définissant simplement le nom d'utilisateur et le mot de passe sur l' NetworkCredentialobjet, l'application a pu se connecter une fois au lecteur réseau. Après cela, nous avons obtenu un ERROR_LOGON_FAILURE à chaque tentative jusqu'au redémarrage de l'application. Nous avons ensuite essayé de fournir le domaine sur l' NetworkCredentialobjet également, et du coup cela a fonctionné! Je ne sais pas pourquoi cela a résolu le problème, en particulier le fait que cela fonctionnait pour se connecter une fois sans le domaine.
lsmepar

50

Aujourd'hui 7 ans plus tard, je suis confronté au même problème et j'aimerais partager ma version de la solution.

Il est prêt pour le copier-coller :-) Le voici:

Étape 1

Dans votre code (chaque fois que vous devez faire quelque chose avec des autorisations)

ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate
                            {
                                //Your code here 
                                //Let's say file copy:
                                if (!File.Exists(to))
                                {
                                    File.Copy(from, to);
                                }
                            });

Étape 2

Le fichier Helper qui fait de la magie

using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;    
using Microsoft.Win32.SafeHandles;


namespace BlaBla
{
    public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle()
            : base(true)
        {
        }

        [DllImport("kernel32.dll")]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        protected override bool ReleaseHandle()
        {
            return CloseHandle(handle);
        }
    }

    public class ImpersonationHelper
    {
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
        int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private extern static bool CloseHandle(IntPtr handle);

        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        public static void Impersonate(string domainName, string userName, string userPassword, Action actionToExecute)
        {
            SafeTokenHandle safeTokenHandle;
            try
            {

                const int LOGON32_PROVIDER_DEFAULT = 0;
                //This parameter causes LogonUser to create a primary token.
                const int LOGON32_LOGON_INTERACTIVE = 2;

                // Call LogonUser to obtain a handle to an access token.
                bool returnValue = LogonUser(userName, domainName, userPassword,
                    LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                    out safeTokenHandle);
                //Facade.Instance.Trace("LogonUser called.");

                if (returnValue == false)
                {
                    int ret = Marshal.GetLastWin32Error();
                    //Facade.Instance.Trace($"LogonUser failed with error code : {ret}");

                    throw new System.ComponentModel.Win32Exception(ret);
                }

                using (safeTokenHandle)
                {
                    //Facade.Instance.Trace($"Value of Windows NT token: {safeTokenHandle}");
                    //Facade.Instance.Trace($"Before impersonation: {WindowsIdentity.GetCurrent().Name}");

                    // Use the token handle returned by LogonUser.
                    using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
                    {
                        using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
                        {
                            //Facade.Instance.Trace($"After impersonation: {WindowsIdentity.GetCurrent().Name}");
                            //Facade.Instance.Trace("Start executing an action");

                            actionToExecute();

                            //Facade.Instance.Trace("Finished executing an action");
                        }
                    }
                    //Facade.Instance.Trace($"After closing the context: {WindowsIdentity.GetCurrent().Name}");
                }

            }
            catch (Exception ex)
            {
                //Facade.Instance.Trace("Oh no! Impersonate method failed.");
                //ex.HandleException();
                //On purpose: we want to notify a caller about the issue /Pavel Kovalev 9/16/2016 2:15:23 PM)/
                throw;
            }
        }
    }
}

2
@MohammadRashid D'après la documentation sur LogonUser , cela ne fonctionne que pour les utilisateurs de l'ordinateur local: "La fonction LogonUser tente de connecter un utilisateur à l'ordinateur local. L'ordinateur local est l'ordinateur à partir duquel LogonUser a été appelé. Vous ne pouvez pas utiliser LogonUser pour vous connecter à un ordinateur distant. "Vous recevrez une erreur" Win32Exception: le nom d'utilisateur ou le mot de passe est incorrect. " Je suppose donc que les machines doivent au moins être sur le même domaine.
Charles Chen

1
@CharlesChen vient de prouver que cela fonctionne bien dans tous les domaines, pour info. Le serveur sur lequel je l'exécute se trouve dans une zone démilitarisée et se connecte définitivement à un serveur de fichiers sur un domaine différent, via un pare-feu. Killer snippet Pavel, vous êtes l'homme, et cela devrait probablement être la réponse acceptée aujourd'hui.
Brian MacKay

C'est une excellente solution! Merci, Pavel Kovalev.
STLDev

cela fonctionne-t-il sur LDAP? il dit que je n'ai pas de serveur de connexion disponible. im using ldap auth
Julius Limson

28

J'ai cherché beaucoup de méthodes et je l'ai fait à ma manière. Vous devez ouvrir une connexion entre deux machines via l'invite de commande NET USE et après avoir terminé votre travail, effacez la connexion avec l'invite de commande NET USE "myconnection" / delete.

Vous devez utiliser le processus d'invite de commande à partir du code derrière comme ceci:

var savePath = @"\\servername\foldername\myfilename.jpg";
var filePath = @"C:\\temp\myfileTosave.jpg";

L'utilisation est simple:

SaveACopyfileToServer(filePath, savePath);

Voici les fonctions:

using System.IO
using System.Diagnostics;


public static void SaveACopyfileToServer(string filePath, string savePath)
    {
        var directory = Path.GetDirectoryName(savePath).Trim();
        var username = "loginusername";
        var password = "loginpassword";
        var filenameToSave = Path.GetFileName(savePath);

        if (!directory.EndsWith("\\"))
            filenameToSave = "\\" + filenameToSave;

        var command = "NET USE " + directory + " /delete";
        ExecuteCommand(command, 5000);

        command = "NET USE " + directory + " /user:" + username + " " + password;
        ExecuteCommand(command, 5000);

        command = " copy \"" + filePath + "\"  \"" + directory + filenameToSave + "\"";

        ExecuteCommand(command, 5000);


        command = "NET USE " + directory + " /delete";
        ExecuteCommand(command, 5000);
    }

Et aussi la fonction ExecuteCommand est:

public static int ExecuteCommand(string command, int timeout)
    {
        var processInfo = new ProcessStartInfo("cmd.exe", "/C " + command)
                              {
                                  CreateNoWindow = true, 
                                  UseShellExecute = false, 
                                  WorkingDirectory = "C:\\",
                              };

        var process = Process.Start(processInfo);
        process.WaitForExit(timeout);
        var exitCode = process.ExitCode;
        process.Close();
        return exitCode;
    } 

Cette fonction a fonctionné très rapidement et de manière stable pour moi.


1
En cas d'échec du mappage de partage, quels seraient les codes de retour?
surega

14

La solution Luke Quinane semble bonne, mais n'a fonctionné que partiellement dans mon application ASP.NET MVC. Ayant deux partages sur le même serveur avec des informations d'identification différentes, je ne pourrais utiliser l'emprunt d'identité que pour le premier.

Le problème avec WNetAddConnection2 est également qu'il se comporte différemment sur différentes versions de Windows. C'est pourquoi j'ai cherché des alternatives et trouvé la fonction LogonUser . Voici mon code qui fonctionne également dans ASP.NET:

public sealed class WrappedImpersonationContext
{
    public enum LogonType : int
    {
        Interactive = 2,
        Network = 3,
        Batch = 4,
        Service = 5,
        Unlock = 7,
        NetworkClearText = 8,
        NewCredentials = 9
    }

    public enum LogonProvider : int
    {
        Default = 0,  // LOGON32_PROVIDER_DEFAULT
        WinNT35 = 1,
        WinNT40 = 2,  // Use the NTLM logon provider.
        WinNT50 = 3   // Use the negotiate logon provider.
    }

    [DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain,
        String lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, ref IntPtr phToken);

    [DllImport("kernel32.dll")]
    public extern static bool CloseHandle(IntPtr handle);

    private string _domain, _password, _username;
    private IntPtr _token;
    private WindowsImpersonationContext _context;

    private bool IsInContext
    {
        get { return _context != null; }
    }

    public WrappedImpersonationContext(string domain, string username, string password)
    {
        _domain = String.IsNullOrEmpty(domain) ? "." : domain;
        _username = username;
        _password = password;
    }

    // Changes the Windows identity of this thread. Make sure to always call Leave() at the end.
    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public void Enter()
    {
        if (IsInContext)
            return;

        _token = IntPtr.Zero;
        bool logonSuccessfull = LogonUser(_username, _domain, _password, LogonType.NewCredentials, LogonProvider.WinNT50, ref _token);
        if (!logonSuccessfull)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        WindowsIdentity identity = new WindowsIdentity(_token);
        _context = identity.Impersonate();

        Debug.WriteLine(WindowsIdentity.GetCurrent().Name);
    }

    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public void Leave()
    {
        if (!IsInContext)
            return;

        _context.Undo();

        if (_token != IntPtr.Zero)
        {
            CloseHandle(_token);
        }
        _context = null;
    }
}

Usage:

var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password);
impersonationContext.Enter();

//do your stuff here

impersonationContext.Leave();

2
cette approche a bien fonctionné pour moi, mais j'ai remarqué lors de mes tests que lors de l'utilisation d'un mauvais mot de passe avec un compte d'utilisateur de domaine, cet utilisateur est immédiatement placé dans le statut verrouillé. notre politique de domaine appelle à 3 tentatives de connexion infructueuses avant que cela ne se produise, mais via cette approche, une mauvaise tentative et vous êtes verrouillé. Alors, utilisez avec prudence ...
kellyb

5

Pour les amateurs de VB, l'équivalent VB.NET du code de Luke Quinane (merci Luke!)

Imports System
Imports System.Net
Imports System.Runtime.InteropServices
Imports System.ComponentModel

Public Class NetworkConnection
    Implements IDisposable

    Private _networkName As String

    Public Sub New(networkName As String, credentials As NetworkCredential)
        _networkName = networkName

        Dim netResource = New NetResource() With {
             .Scope = ResourceScope.GlobalNetwork,
             .ResourceType = ResourceType.Disk,
             .DisplayType = ResourceDisplaytype.Share,
             .RemoteName = networkName
        }

        Dim userName = If(String.IsNullOrEmpty(credentials.Domain), credentials.UserName, String.Format("{0}\{1}", credentials.Domain, credentials.UserName))

        Dim result = WNetAddConnection2(NetResource, credentials.Password, userName, 0)

        If result <> 0 Then
            Throw New Win32Exception(result, "Error connecting to remote share")
        End If
    End Sub

    Protected Overrides Sub Finalize()
        Try
            Dispose (False)
        Finally
            MyBase.Finalize()
        End Try
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose (True)
        GC.SuppressFinalize (Me)
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        WNetCancelConnection2(_networkName, 0, True)
    End Sub

    <DllImport("mpr.dll")> _
    Private Shared Function WNetAddConnection2(netResource As NetResource, password As String, username As String, flags As Integer) As Integer
    End Function

    <DllImport("mpr.dll")> _
    Private Shared Function WNetCancelConnection2(name As String, flags As Integer, force As Boolean) As Integer
    End Function

End Class

<StructLayout(LayoutKind.Sequential)> _
Public Class NetResource
    Public Scope As ResourceScope
    Public ResourceType As ResourceType
    Public DisplayType As ResourceDisplaytype
    Public Usage As Integer
    Public LocalName As String
    Public RemoteName As String
    Public Comment As String
    Public Provider As String
End Class

Public Enum ResourceScope As Integer
    Connected = 1
    GlobalNetwork
    Remembered
    Recent
    Context
End Enum

Public Enum ResourceType As Integer
    Any = 0
    Disk = 1
    Print = 2
    Reserved = 8
End Enum

Public Enum ResourceDisplaytype As Integer
    Generic = &H0
    Domain = &H1
    Server = &H2
    Share = &H3
    File = &H4
    Group = &H5
    Network = &H6
    Root = &H7
    Shareadmin = &H8
    Directory = &H9
    Tree = &HA
    Ndscontainer = &HB
End Enum

3

Une option qui pourrait fonctionner consiste à utiliser WindowsIdentity.Impersonate(et à modifier le principal du thread) pour devenir l'utilisateur souhaité, comme cela . De retour à p / invoke, j'ai bien peur ...

Une autre option effrontée (et tout aussi loin d'être idéale) pourrait être de créer un processus pour faire le travail ... ProcessStartInfoaccepte un .UserName, .Passwordet .Domain.

Enfin - peut-être exécuter le service dans un compte dédié qui y a accès? (supprimé car vous avez précisé que ce n'est pas une option).


Je ne pense pas que le processus soit une si mauvaise idée. google a publié des livres blancs sur les avantages du multitraitement dans Chrome.
Dustin Getz

Est-il possible de changer le principal du thread en un utilisateur sans compte sur la machine locale?
gyrolf

Pour être honnête, je ne sais tout simplement pas ... Vous devrez essayer LogonUser avec un domaine différent pour le savoir.
Marc Gravell

3

OK ... je peux répondre ...

Avertissement: Je viens de passer une journée de plus de 18 heures (à nouveau) .. Je suis vieux et oublieux .. Je ne peux pas épeler .. J'ai une capacité d'attention courte, donc je ferais mieux de répondre rapidement .. :-)

Question:

Est-il possible de changer le principal du thread en un utilisateur sans compte sur la machine locale?

Répondre:

Oui, vous pouvez modifier un principal de thread même si les informations d'identification que vous utilisez ne sont pas définies localement ou sont en dehors de la «forêt».

Je viens de rencontrer ce problème en essayant de me connecter à un serveur SQL avec l'authentification NTLM à partir d'un service. Cet appel utilise les informations d'identification associées au processus, ce qui signifie que vous avez besoin d'un compte local ou d'un compte de domaine pour vous authentifier avant de pouvoir emprunter l'identité. Bla, bla ...

Mais...

L'appel de LogonUser (..) avec l'attribut ???? _ NEW_CREDENTIALS renverra un jeton de sécurité sans essayer d'authentifier les informations d'identification. Kewl .. Pas besoin de définir le compte dans la "forêt". Une fois que vous avez le jeton, vous devrez peut-être appeler DuplicateToken () avec l'option d'activer l'emprunt d'identité résultant en un nouveau jeton. Appelez maintenant SetThreadToken (NULL, token); (Cela peut être & token?) .. Un appel à ImpersonateLoggedonUser (token); pourrait être nécessaire, mais je ne pense pas. Cherchez-le..

Faites ce que vous devez faire.

Appelez RevertToSelf () si vous avez appelé ImpersonateLoggedonUser () puis SetThreadToken (NULL, NULL); (Je pense ... cherchez-le), puis CloseHandle () sur les poignées créées ..

Aucune promesse mais cela a fonctionné pour moi ... C'est du dessus de ma tête (comme mes cheveux) et je ne peux pas épeler !!!



1

Également porté en F # pour être utilisé avec FAKE

module NetworkShare

open System
open System.ComponentModel
open System.IO
open System.Net
open System.Runtime.InteropServices

type ResourceScope =
| Connected = 1
| GlobalNetwork = 2
| Remembered = 3
| Recent = 4
type ResourceType =
| Any = 0
| Disk = 1
| Print = 2
| Reserved = 8
type ResourceDisplayType =
| Generic = 0x0
| Domain = 0x01
| Server = 0x02
| Share = 0x03
| File = 0x04
| Group = 0x05
| Network = 0x06
| Root = 0x07
| Shareadmin = 0x08
| Directory = 0x09
| Tree = 0x0a
| Ndscontainer = 0x0b

//Uses of this construct may result in the generation of unverifiable .NET IL code.
#nowarn "9"
[<StructLayout(LayoutKind.Sequential)>]
type NetResource =
  struct
    val mutable Scope : ResourceScope
    val mutable ResourceType : ResourceType
    val mutable DisplayType : ResourceDisplayType
    val mutable Usage : int
    val mutable LocalName : string
    val mutable RemoteName : string
    val mutable Comment : string
    val mutable Provider : string
    new(name) = {
      // lets preset needed fields
      NetResource.Scope = ResourceScope.GlobalNetwork
      ResourceType = ResourceType.Disk
      DisplayType = ResourceDisplayType.Share
      Usage = 0
      LocalName = null
      RemoteName = name
      Comment = null
      Provider = null
    }
  end

type WNetConnection(networkName : string, credential : NetworkCredential) =
  [<Literal>]
  static let Mpr = "mpr.dll"
  [<DllImport(Mpr, EntryPoint = "WNetAddConnection2")>]
  static extern int connect(NetResource netResource, string password, string username, int flags)
  [<DllImport(Mpr, EntryPoint = "WNetCancelConnection2")>]
  static extern int disconnect(string name, int flags, bool force)

  let mutable disposed = false;

  do
    let userName = if String.IsNullOrWhiteSpace credential.Domain
                   then credential.UserName
                   else credential.Domain + "\\" + credential.UserName
    let resource = new NetResource(networkName)

    let result = connect(resource, credential.Password, userName, 0)

    if result <> 0 then
      let msg = "Error connecting to remote share " + networkName
      new Win32Exception(result, msg)
      |> raise

  let cleanup(disposing:bool) =
    if not disposed then
      disposed <- true
      if disposing then () // TODO dispose managed resources here
      disconnect(networkName, 0, true) |> ignore

  interface IDisposable with
    member __.Dispose() =
      disconnect(networkName, 0, true) |> ignore
      GC.SuppressFinalize(__)

  override __.Finalize() = cleanup(false)

type CopyPath =
  | RemotePath of string * NetworkCredential
  | LocalPath of string

let createDisposable() =
  {
    new IDisposable with
      member __.Dispose() = ()
  }

let copyFile overwrite destPath srcPath : unit =
  use _srcConn =
    match srcPath with
    | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
    | LocalPath(_) -> createDisposable()
  use _destConn =
    match destPath with
    | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
    | LocalPath(_) -> createDisposable()
  match srcPath, destPath with
  | RemotePath(src, _), RemotePath(dest, _)
  | LocalPath(src), RemotePath(dest, _)
  | RemotePath(src, _), LocalPath(dest)
  | LocalPath(src), LocalPath(dest) ->
    if FileInfo(src).Exists |> not then
      failwith ("Source file not found: " + src)
    let destFilePath =
      if DirectoryInfo(dest).Exists then Path.Combine(dest, Path.GetFileName src)
      else dest
    File.Copy(src, destFilePath, overwrite)

let rec copyDir copySubDirs filePattern destPath srcPath =
  use _srcConn =
    match srcPath with
    | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
    | LocalPath(_) -> createDisposable()
  use _destConn =
    match destPath with
    | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable
    | LocalPath(_) -> createDisposable()
  match srcPath, destPath with
  | RemotePath(src, _), RemotePath(dest, _)
  | LocalPath(src), RemotePath(dest, _)
  | RemotePath(src, _), LocalPath(dest)
  | LocalPath(src), LocalPath(dest) ->
    let dir = DirectoryInfo(src)
    if dir.Exists |> not then
      failwith ("Source directory not found: " + src)

    let dirs = dir.GetDirectories()
    if Directory.Exists(dest) |> not then
      Directory.CreateDirectory(dest) |> ignore

    let files = dir.GetFiles(filePattern)
    for file in files do
      let tempPath = Path.Combine(dest, file.Name)
      file.CopyTo(tempPath, false) |> ignore

    if copySubDirs then
      for subdir in dirs do
        let subdirSrc =
          match srcPath with
          | RemotePath(_, credential) -> RemotePath(Path.Combine(dest, subdir.Name), credential)
          | LocalPath(_) -> LocalPath(Path.Combine(dest, subdir.Name))
        let subdirDest =
          match destPath with
          | RemotePath(_, credential) -> RemotePath(subdir.FullName, credential)
          | LocalPath(_) -> LocalPath(subdir.FullName)
        copyDir copySubDirs filePattern subdirDest subdirSrc

0

Vous devriez envisager d'ajouter un comme celui-ci:

<identity impersonate="true" userName="domain\user" password="****" />

Dans votre web.config.

Plus d'information.


Certains services de sécurité d'entreprise empêchent l'utilisation de l'emprunt d'identité car ils ne peuvent pas suivre l'application qui l'utilise et doivent appartenir au même domaine ou à un domaine approuvé. Je pense qu'un soutien imité est repéré. Un compte de service de domaine avec pinvoke semble être la voie à suivre.
Jim
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.