views:

64

answers:

1

I have an Action that needs to read a file from a secure location, so I have to use impersonation to read the file.

This code WORKS:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult DirectDownload(Guid id)
{
    if (Impersonator.ImpersonateValidUser())
    {
        try
        {
            var path = "path to file";
            if (!System.IO.File.Exists(path))
            {
                return View("filenotfound");
            }

            var bytes = System.IO.File.ReadAllBytes(path);
            return File(bytes, "application/octet-stream", "FileName");
        }
        catch (Exception e)
        {
            Log.Exception(e);
        }finally
        {
            Impersonator.UndoImpersonation();
        }
    }
    return View("filenotfound");
}

The only problem with the above code is that I have to read the entire file into memory and I am going to be dealing with VERY large files, so this is not a good solution. But if I replace these 2 lines:

var bytes = System.IO.File.ReadAllBytes(path);
return File(bytes, "application/octet-stream", "FileName");

with this:

return File(path, "application/octet-stream", "FileName");

It does NOT work and I get the error message:

Access to the path 'c:\projects\uploads\1\aa2bcbe7-ea99-499d-add8-c1fdac561b0e\Untitled 2.csv' is denied.

I guess using the File results with a path, tries to open the file at a later time in the request pipeline when I have already "undone" the impersonation.

Remember, the impersonation code works because I can read the file in the bytes array. What I want to do though is stream the file to the client.

Any idea how I can work around this?

Thanks in advance.

+1  A: 

You may try writing a custom FilePathResult:

public class ImpersonatingFileResult : FilePathResult
{
    public ImpersonatingFileResult(string fileName, string contentType) 
        : base(fileName, contentType)
    { }

    protected override void WriteFile(HttpResponseBase response)
    {
        // TODO : Start impersonation
        response.TransmitFile(FileName);
        // TODO : Rollback impersonation
    }
}

and in your controller:

return new ImpersonatingFileResult(path, "application/octet-stream");
Darin Dimitrov
That worked great. I changed it a little because I was getting an exception... Impersonator.ImpersonateValidUser(); response.WriteFile(FileName); response.Flush(); response.End(); Impersonator.UndoImpersonation();
eibrahim