Cómo proporcionar nombre de usuario y contraseña cuando se conecta a un recurso compartido de red

191

Cuando se conecta a un recurso compartido de red para el cual el usuario actual (en mi caso, un usuario de servicio habilitado para la red) no tiene derechos, se deben proporcionar nombre y contraseña.

Sé cómo hacer esto con las funciones de Win32 (la WNet*familia de mpr.dll), pero me gustaría hacerlo con la funcionalidad .Net (2.0).

¿Qué opciones hay disponibles?

Tal vez más información ayude:

  • El caso de uso es un servicio de Windows, no una aplicación Asp.Net.
  • El servicio se ejecuta bajo una cuenta que no tiene derechos sobre el recurso compartido.
  • La cuenta de usuario necesaria para el recurso compartido no se conoce en el lado del cliente.
  • El cliente y el servidor no son miembros del mismo dominio.
gyrolf
fuente
77
Si bien no le estoy dando una respuesta útil, puedo proporcionarle una respuesta anti ... Suplantar y generar un proceso como Marc postuló no funcionará cuando el servidor y el cliente no estén en el mismo dominio, a menos que haya una confianza entre Los dos dominios. Si hay una confianza, creo que funcionará. Hubiera respondido como un comentario a Marc's pero no tengo suficiente representante para comentar. : - /
Moose

Respuestas:

152

Puede cambiar la identidad del hilo o P / Invocar WNetAddConnection2. Prefiero el último, ya que a veces necesito mantener múltiples credenciales para diferentes ubicaciones. Lo envuelvo en un IDisposable y llamo a WNetCancelConnection2 para eliminar los créditos después (evitando el error de múltiples nombres de usuario):

using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
   File.Copy(@"\\server\read\file", @"\\server2\write\file");
}
Mark Brackett
fuente
44
El servicio no es miembro del dominio de destino: la suplantación no puede funcionar ya que no podría crear el token de seguridad localmente y suplantarlo. PInvoke es la única forma.
stephbu
@MarkBrackett Sé que esta es una respuesta antigua, pero tal vez todavía lo sepa ... ¿se otorgará el acceso solo al programa o también al usuario que inició sesión a través del explorador?
Brisa
@Breeze: no lo he probado, pero espero que se autentique para la sesión de inicio de sesión; por lo tanto, si su programa se ejecuta como el usuario que inició sesión, también tendrían acceso (al menos durante la operación).
Mark Brackett
8
Las definiciones de readCredentials y writeCredentials podrían incluirse en la respuesta.
Anders Lindén
2
Si obtiene el error 53 , asegúrese de que el camino no termine con un "\"
Mustafa S.
326

Me gustó tanto la respuesta de Mark Brackett que hice mi propia implementación rápida. Aquí está si alguien más lo necesita a toda prisa:

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
}
Luke Quinane
fuente
10
Realmente debería serlo throw new Win32Exception(result);, ya que WNetAddConnection2 devuelve códigos de error win32 ( ERROR_XXX)
torvin
2
Este es un pequeño código brillante. Necesitaba iniciar sesión en un sistema UNIX para obtener una lista de directorios para imprimir en una aplicación web MVC5 y esto fue el truco. +1 !!!
Tay
3
Las siguientes instrucciones de uso son necesarias para que el código anterior se compile: using System.Net; usando System.Runtime.InteropServices; utilizando System.ComponentModel;
Matt Nelson
44
lamento actualizar ese hilo anterior, pero parece que no cierra la conexión una vez que finaliza el bloqueo. Tengo un programa para subir algunas fotos, la primera sale bien y la segunda falla. La conexión se libera cuando se cierra el programa. ¿Algún consejo?
arti
3
Tuvimos el mismo problema que tú, @arti. Simplemente configurando el nombre de usuario y la contraseña en el NetworkCredentialobjeto, la aplicación pudo conectarse una vez a la unidad de red. Después de eso obtuvimos un ERROR_LOGON_FAILURE en cada intento hasta que se reinició la aplicación. Luego intentamos proporcionar el dominio en el NetworkCredentialobjeto también, ¡y de repente funcionó! No tengo idea de por qué esto solucionó el problema, especialmente el hecho de que funcionó para conectarse una vez sin el dominio.
lsmeby
50

Hoy, 7 años después, me enfrento al mismo problema y me gustaría compartir mi versión de la solución.

Está listo para copiar y pegar :-) Aquí está:

Paso 1

En su código (siempre que necesite hacer algo con permisos)

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

Paso 2

El archivo de ayuda que hace una magia

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;
            }
        }
    }
}
Pavel Kovalev
fuente
2
@MohammadRashid De acuerdo con la documentación de LogonUser , solo funciona para usuarios en la computadora local: "La función LogonUser intenta iniciar la sesión de un usuario en la computadora local. La computadora local es la computadora desde la cual se llamó a LogonUser. No puede usar LogonUser. para iniciar sesión en una computadora remota. "Recibirá un error" Win32Exception: el nombre de usuario o la contraseña son incorrectos ". Así que supongo que las máquinas deben estar en el mismo dominio al menos.
Charles Chen el
1
@CharlesChen Acaba de demostrar que esto funciona bien en todos los dominios, para su información. El servidor en el que estoy ejecutando esto está en una DMZ, y definitivamente se está conectando a un servidor de archivos en un dominio diferente, a través de un firewall. Fragmento asesino Pavel, tú eres el hombre, y esta probablemente debería ser la respuesta aceptada hoy.
Brian MacKay
¡Esta es una GRAN SOLUCIÓN! Gracias Pavel Kovalev.
STLDev
funciona esto en ldap? dice que no tengo un servidor de inicio de sesión disponible. estoy usando autenticación ldap
Julius Limson
28

Busqué muchos métodos y lo hice a mi manera. Tiene que abrir una conexión entre dos máquinas a través del comando NET USE del símbolo del sistema y después de terminar su trabajo, borre la conexión con el comando NET USE "myconnection" / delete.

Debe usar el proceso del símbolo del sistema desde el código detrás de esta manera:

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

El uso es simple:

SaveACopyfileToServer(filePath, savePath);

Aquí hay funciones:

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);
    }

Y también la función ExecuteCommand es:

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;
    } 

Esta función funcionó muy rápido y estable para mí.

Hakan KOSE
fuente
1
En caso de que falle la asignación de recursos compartidos, ¿cuáles serían los códigos de retorno?
surega
14

La solución de Luke Quinane se ve bien, pero funcionó solo parcialmente en mi aplicación ASP.NET MVC. Teniendo dos recursos compartidos en el mismo servidor con credenciales diferentes, podría usar la suplantación solo para el primero.

El problema con WNetAddConnection2 también es que se comporta de manera diferente en diferentes versiones de Windows. Es por eso que busqué alternativas y encontré la función LogonUser . Aquí está mi código que también funciona en 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;
    }
}

Uso:

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

//do your stuff here

impersonationContext.Leave();
VladL
fuente
2
Este enfoque funcionó bien para mí, pero noté en mis pruebas que al usar una contraseña incorrecta con una cuenta de usuario de dominio, ese usuario queda inmediatamente en estado bloqueado. nuestra política de dominio requiere 3 intentos fallidos de inicio de sesión antes de que eso suceda, pero a través de este enfoque un mal intento y estás bloqueado. Entonces, use con precaución ...
kellyb
5

Para VB.lovers, el equivalente VB.NET del código de Luke Quinane (¡gracias 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
Alessandro Bernardi
fuente
3

Una opción que podría funcionar es usar WindowsIdentity.Impersonate(y cambiar el hilo principal) para convertirse en el usuario deseado, como así . De vuelta a p / invoke, sin embargo, me temo ...

Otra opción descarada (e igualmente lejos de ser ideal) podría ser generar un proceso para hacer el trabajo ... ProcessStartInfoacepta un .UserName, .Passwordy.Domain .

Finalmente, ¿tal vez ejecutar el servicio en una cuenta dedicada que tenga acceso? (eliminado ya que ha aclarado que esto no es una opción).

Marc Gravell
fuente
No creo que el proceso sea una mala idea. Google publicó algunos libros blancos sobre los beneficios del multiprocesamiento en Chrome.
Dustin Getz
¿Es posible cambiar el hilo principal a un usuario sin cuenta en la máquina local?
gyrolf
Para ser honesto, simplemente no sé ... Tendrías que probar LogonUser con un dominio diferente para averiguarlo.
Marc Gravell
3

OK ... puedo volver a vender ...

Descargo de responsabilidad: Acabo de pasar un día de más de 18 horas (de nuevo) ... Soy viejo y olvidadizo ... No puedo deletrear ... Tengo un período de atención corto, así que mejor respondo rápido ... :-)

Pregunta:

¿Es posible cambiar el hilo principal a un usuario sin cuenta en la máquina local?

Responder:

Sí, puede cambiar un hilo principal incluso si las credenciales que está utilizando no están definidas localmente o están fuera del "bosque".

Me encontré con este problema al intentar conectarme a un servidor SQL con autenticación NTLM desde un servicio. Esta llamada utiliza las credenciales asociadas con el proceso, lo que significa que necesita una cuenta local o una cuenta de dominio para autenticarse antes de poder suplantar. Bla, bla ...

Pero...

Llamar a LogonUser (..) con el atributo ???? _ NEW_CREDENTIALS devolverá un token de seguridad sin intentar autenticar las credenciales. Kewl .. No tiene que definir la cuenta dentro del "bosque". Una vez que tenga el token, es posible que tenga que llamar a DuplicateToken () con la opción de habilitar la suplantación que da como resultado un nuevo token. Ahora llame a SetThreadToken (NULL, token); (¿Podría ser & token?) .. Una llamada a ImpersonateLoggedonUser (token); puede ser requerido, pero no lo creo. Búscalo ..

Haz lo que necesitas hacer..

Llame a RevertToSelf () si llamó a ImpersonateLoggedonUser () y luego a SetThreadToken (NULL, NULL); (Creo ... búscalo), y luego CloseHandle () en los controladores creados ...

No hay promesas, pero esto funcionó para mí ... ¡Esto está fuera de mi cabeza (como mi cabello) y no puedo deletrear!

Craig Armstrong
fuente
1

También portado a F # para usar con 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
python_kaa
fuente
0

Debería buscar agregar un me gusta como este:

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

En su web.config.

Más información.

GEOCHET
fuente
Parte de la seguridad corporativa impide el uso de suplantación de identidad porque no pueden rastrear la aplicación que la usa y deben estar en el mismo dominio o en el de confianza. Creo que el apoyo de suplantación ha sido visto. Una cuenta de servicio de dominio con pinvoke parece ser el camino a seguir.
Jim