Reputation: 1
I am developing an Umbraco intranet site where I call wkhtmltopdf.exe to create some pdfs. Those pdfs are using a main page for the content and two additional pages for header and footer. Things worked pretty fine as long as I had the site without authentication. We want to use our Active Directory accounts to login to the site and thus I have enabled windows authentication. The routine for running this is to click a button that process the page and either shows the pdf on the browser or downloads it. In any case it is the same process. In visual studio when running with debugging when it comes to the first part of the code (var p = ...) it throws an exception "Message = "No process is associated with this object." because it fails to authenticate. I can see that when I pause the code just after its execution and using the visual studio inspector. The method runs to the end but because of the error I mentioned before it produces a blank pdf. If I hardcode username and password then it works fine.
Site is running in my local dev enviroment in iis express. Since Windows Authentication is enabled when I browse to the site the first time I have to login. Wkhtmltopdf.exe is located in the local drive - it is not on the website. The initial setup is based on the method described here http://icanmakethiswork.blogspot.se/2012/04/making-pdfs-from-html-in-c-using.html Only users that are part of our Active Directory domain will have access to the website but since we use the same accounts to login to windows then windows authentication will do the trick :)
public static void HtmlToPdf(string outputFilename, string[] urls,
string[] options = null,
bool streamPdf = false,
string pdfHtmlToPdfExePath = "C:\\Program Files (x86)\\wkhtmltopdf\\bin\\wkhtmltopdf.exe")
{
string urlsSeparatedBySpaces = string.Empty;
try
{
//Determine inputs
if ((urls == null) || (urls.Length == 0))
{
throw new Exception("No input URLs provided for HtmlToPdf");
}
urlsSeparatedBySpaces = String.Join(" ", urls); //Concatenate URLs
var p = new System.Diagnostics.Process()
{
StartInfo =
{
FileName = pdfHtmlToPdfExePath,
Arguments = ((options == null) ? "" : String.Join(" ", options)) + " " + urlsSeparatedBySpaces + " -",
UseShellExecute = false, // needs to be false in order to redirect output
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true, // redirect all 3, as it should be all 3 or none
WorkingDirectory = string.Empty
}
};
p.Start();
var output = p.StandardOutput.ReadToEnd();
byte[] buffer = p.StandardOutput.CurrentEncoding.GetBytes(output);
p.WaitForExit(60000);
p.Close();
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = "application/pdf";
if (!streamPdf)
{
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename='" + outputFilename + "'");
}
HttpContext.Current.Response.BinaryWrite(buffer);
HttpContext.Current.Response.End();
}
catch (Exception exc)
{
throw new Exception("Problem generating PDF from HTML, URLs: " + urlsSeparatedBySpaces + ", outputFilename: " + outputFilename, exc);
}
}
I tested this with LoadUserProfile = true but that didnt't help also. After reading throughout various forum posts the only suggested solution that I saw was to force loging in the process by using UserName, Password etc. But that is bad since the user is already logged in and we could/should use something like CredentialCache.DefaultCredentials .
A workaround that I came too was to use DefaultCredentials in requests to save the htmls locally where I can access them without a problem and create the pdfs but even that is a painstaking process, since i need to create printable css and javascripts and download them etc etc. This is my last solution which I have implemented at 80% but seems nasty also. Here is another code sample how I grab the webpages.
var request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
var response = (HttpWebResponse)request.GetResponse();
var stream = response.GetResponseStream();
So to sum up. Wkhtmltopdf fails to authenticate itself so that it can grab the desired pages and turn them to pdf. Is there any neat way to make the process able to authenticate itself with current user's credentials that I am logged in to the site so that it can access the pages?
Upvotes: 0
Views: 2579
Reputation: 885
I Use Rotativa a wrapper for Wkhtmltopdf.
To get it working on iis, I created a separate user account with enough access to run Wkhtmltopdf.exe. Then Pass the User Name & Password to Wkhtmltopdf with the switches.
public virtual ActionResult PrintInvoice(int id) {
var invoice = db.Invoices.Single(i => i.InvoiceId == id);
var viewmodel = Mapper.Map<InvoiceViewModel>(invoice);
var reportName = string.Format(@"Invoice {0:I-000000}.pdf", invoice.InvoiceNo);
var switches = String.Format(@" --print-media-type --username {0} --password {1} ",
ConfigurationManager.AppSettings["PdfUserName"],
ConfigurationManager.AppSettings["PdfPassword"]);
ViewBag.isPDF = true;
return new ViewAsPdf("InvoiceDetails", viewmodel) {
FileName = reportName,
PageOrientation = Rotativa.Options.Orientation.Portrait,
PageSize = Rotativa.Options.Size.A4,
CustomSwitches = switches
};
}
It appears the pages running within Wkhtmltopdf.exe run with the current users credentials, but the Wkhtmltopdf.exe itself needs rights to execute on the server.
This works on iis when deployed. On Cassini in VS2012 it works for me with no credentials, but in vs2013 on iis express I'm still having trouble when it comes to picking up resources like css & images.
Same solution to run over SSL: Rotativa and wkhtmltopdf no CSS or images on iis6 over HTTPS, but fine on HTTP
Upvotes: 2
Reputation: 792
Turn on ASP.NET Impersonation and spawn wkhtmltopdf under the context of the impersonated user.
Note: turning on ASP.NET impersonation is very likely to decrease performance.
Upvotes: 0