ASP.NET MVC: Upload file to network share using username and password

Error:

Access to the path '...' is denied.

Solution:

After struggling with this one for a few hours I found that the problem was passing the right combination of credentials to the network resource, i.e. impersonation AND network credentials, both of which require interop services. One or the other just didn't seem to work on the domain.

  1. Add read/write permission on the share (and in security) for the domain account you will be using to access the resource (this method assumes you are running ASP.NET under the default machine account and that it has not been granted access)
  2. Add the following code as class files to your project:
  3. Add the main code (and update to use your settings):
    ...
    using System.Net; // need this for NetworkCredential
    ...
    
    public ActionResult FileUpload(int id, FormCollection collection)
    {
        if (Request.Files.Count > 0)
        {
            // your configuration here...
            var uploadfolder = "\\networkpath\share";
            var username = "username";
            var password = "password";
            var domain = "domain";
    
            var file = Request.Files[0];
            var pers = new Impersonation();
            pers.Impersonate(username, password, domain);
            var credential = new NetworkCredential(username, password, domain);
            using (new NetworkConnection(destination, credential))
            {
                var path = uploadfolder;
    
                // get file name, IE includes full path so cut it off 
                var parts = file.FileName.Split('\\');
                var fileName = parts[parts.Length - 1];
    
                file.SaveAs(Path.Combine(path, fileName));
            }
    
            pers.Undo();
        }
        
        return View();
    }

ASP.NET: Accessing network resources by username and password

Example:

var readCredentials = new NetworkCredential(username, password, domain);
var writeCredentials = new NetworkCredential(username, password, domain);
using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
   File.Copy(@"\\server\read\file", @"\\server2\write\file");
}

Code:

using System;
using System.IO;
using System.Net;
using System.ComponentModel;
using System.Runtime.InteropServices;

/* Resourced from:
 * http://stackoverflow.com/questions/295538/how-to-provide-user-name-and-password-when-connecting-to-a-network-share 
*/
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 result = WNetAddConnection2(
            netResource,
            credentials.Password,
            credentials.UserName,
            0);

        if (result != 0)
        {
            new Win32Exception(result, "Error connecting to remote share (Error Code " + result.ToString() + ")");
        }
    }

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

Sourced from: http://stackoverflow.com/questions/295538/how-to-provide-user-name-and-password-when-connecting-to-a-network-share

ASP.NET: Impersonation

Impersonate a user (or authenticate a user) in Active Directory or on the Local Machine.

Usage:

var pers = new Impersonate(username, password, domain); // being running under user account
// your code here
pers.Undo(); // revert to default account

Code:

using System.Web.Security;
using System.Security.Principal;
using System.Runtime.InteropServices;

public class Impersonation
{
    public const int LOGON32_PROVIDER_DEFAULT = 0; 
    public const int LOGON32_LOGON_INTERACTIVE = 2; // local machine accounts only
    public const int LOGON32_LOGON_NETWORK = 3; // active directory only
    public const int LOGON32_LOGON_NEW_CREDENTIALS = 9; // always works (useless)

    public WindowsImpersonationContext impersonationContext;
    public WindowsIdentity windowsIdentity;

    [DllImport("advapi32.dll")] // unicode
    public static extern int LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll")] // ascii
    public static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

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

    public bool Impersonate(String username, String password, String domain)
    {
        IntPtr token = IntPtr.Zero;
        if (RevertToSelf())
        {
            if (LogonUser(username, domain, password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                windowsIdentity = new WindowsIdentity(token);
                impersonationContext = windowsIdentity.Impersonate();
                if (impersonationContext != null)
                {
                    CloseHandle(token);
                    return true;
                }
            }
        }
        
        //// throw error if there was one (debug only) instead of returning false
        //var intRetval = Marshal.GetLastWin32Error();
        //if (intRetval > 0)
        //    throw new System.ComponentModel.Win32Exception(intRetval);

        // clean up
        if (token != IntPtr.Zero)
            CloseHandle(token);

        if (impersonationContext != null)
            return false;

        return false;
    }

    public void Undo()
    {
        if (impersonationContext != null)
            impersonationContext.Undo();
    }
}

TSQL: Comma Separated Values

Comma Separated Values in a single column.
SELECT 
    [ID] = t1.Table1ID,
    [CommaSeparatedValues] = REVERSE(STUFF(REVERSE((
  SELECT ColumnName + ',' FROM Table2 t2
  WHERE t2.Table1ID = t1.Table1ID
  FOR XML PATH(''))), 1, 1, ''))
FROM Table1 t1