Authentication fail error on convert Html to Pdf - asp.net-mvc

I try to convert Html to PDF use "HtmlToPdf" nuget , It was work fine on local test but when i upload site to host i get this error :
Conversion error: Authentication error.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Exception: Conversion error: Authentication error.
This is my Convert method Code
[AllowAnonymous]
public ActionResult Convert(int id)
{
HtmlToPdf converter = new HtmlToPdf();
var context = System.Web.HttpContext.Current;
string baseUrl = context.Request.Url.Host + ":"+context.Request.Url.Port + "/Doctor/DietTherapy/LineRegimePrint/";
PdfDocument doc = converter.ConvertUrl(baseUrl + id);
// save pdf document
byte[] pdf = doc.Save();
// close pdf document
doc.Close();
// return resulted pdf document
FileResult fileResult = new FileContentResult(pdf, "application/pdf");
fileResult.FileDownloadName = "Document.pdf";
return fileResult;
}
How can i authorize user for this convert ?

It sounds like you just need to authenticate the request being made by the PDF library. For example, if it's using Basic HTTP Authentication:
HtmlToPdf converter = new HtmlToPdf();
converter.Options.Authentication.Username = "some username";
converter.Options.Authentication.Password = "some password";
// the rest of your code...
The linked documentation also contains examples for other authentication methods.

Did you check to see if you're getting the proper value on this line of code (when running on host server)?
string baseUrl = context.Request.Url.Host + ":"+context.Request.Url.Port + "/Doctor/DietTherapy/LineRegimePrint/";

While your development machine's IIS Express uses Anonymous Authentication, your hosting server is probably using Windows Authentication or other kinds. Check the IIS Managers to see the difference.
SelectPDF's library somehow creates a separate process to perform the HtmlToPdf conversion, which is outside the IUser, and therefore, the server asks for authentication.
For Windows Authentication setting, you may just use a generic user login account for this purpose and populate the required properties as mentioned in above thread and all would be fine.
converter.Options.Authentication.Username = "WindowsUser";
converter.Options.Authentication.Password = "WindowsPassword";

Related

Response Header issue on Azure Web Application

I am not sure what is happening here.
When I run my web application locally and click a button to download a file, the file is downloaded fine and Response header as you can see in the attached screenshot where it says local.
But when I publish the application to azure web app. Somehow the download button stops working. I checked the Response Header and you can see the difference.
What would cause this problem? The code is the same? Is there any settings that I should be setting in azure web app in azure portal?
Updated to add code
I have debugged remotely to figure out what is going on as #Amor suggested.
It is so strange that When I debug on my local machine first ExportTo action gets hit which prepares the TempData then Download action gets called once the first action completed with ajax call.
However, this is not the case when I debug remotely. Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.
But why? Why on earth and how that is possible? Is there something cached somewhere?
I have wiped the content of web application on the remote and re-publish evertyhing to ensure everything is updated. But still no success.
here is the code:
[HttpPost]
public virtual ActionResult ExportTo(SearchVm searchVm)
{
var data = _companyService.GetCompanieBySearchTerm(searchVm).Take(150).ToList();
string handle = Guid.NewGuid().ToString();
TempData[handle] = data;
var fileName = $"C-{handle}.xlsx";
var locationUrl = Url.Action("Download", new { fileGuid = handle, fileName });
var downloadUrl = Url.Action("Download");
return Json(new { success = true, locationUrl, guid = handle, downloadUrl }, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public ActionResult Download(string fileGuid, string fileName)
{
if (TempData[fileGuid] != null)
{
var fileNameSafe = $"C-{fileGuid}.xlsx";
var data = TempData[fileGuid] as List<Company>;
using (MemoryStream ms = new MemoryStream())
{
GridViewExtension.WriteXlsx(GetGridSettings(fileNameSafe), data, ms);
MVCxSpreadsheet mySpreadsheet = new MVCxSpreadsheet();
ms.Position = 0;
mySpreadsheet.Open("myDoc", DocumentFormat.Xlsx, () =>
{
return ms;
});
mySpreadsheet.Document.Worksheets.Insert(0);
var image = Server.MapPath("~/images/logo.png");
var worksheet = mySpreadsheet.Document.Worksheets[0];
worksheet.Name = "Logo";
worksheet.Pictures.AddPicture(image, worksheet.Cells[0, 0]);
byte[] result = mySpreadsheet.SaveCopy(DocumentFormat.Xlsx);
DocumentManager.CloseDocument("myDoc");
Response.Clear();
//Response.AppendHeader("Set-Cookie", "fileDownload=true; path=/");
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", $"attachment; filename={fileNameSafe}");
Response.BinaryWrite(result);
Response.End();
}
}
return new EmptyResult();
}
here is the javascript:
var exportData = function (urlExport) {
console.log('Export to link in searchController: ' + urlExport);
ExportButton.SetEnabled(false);
var objData = new Object();
var filterData = companyFilterData(objData);
console.log(filterData);
$.post(urlExport, filterData)
.done(function (data) {
console.log(data.locationUrl);
window.location.href = data.locationUrl;
});
};
When Export button is clicked exportData function is called:
var exportToLink = '#Url.Action("ExportTo")';
console.log('Export to link in index: '+exportToLink);
SearchController.exportData(exportToLink);
As I mentioned that this code works perfectly on the local machine. something weird is happening on azure webapp that ExportTo action breakpoint is never gets hit.
I am not sure what else I could change to get the ExportTo action hit?
Based on the Response Header of Azure Web App, we find that the value of Content-Length is 0. It means that no data has been sent from web app server side.
In ASP.NET MVC, we can response file using following ways.
The first way, send the file which hosted on server. For this way, please check whether the excel file has been uploaded to Azure Web App. You could use Kudu or FTP to the folder to check whether the file is exist.
string fileLocation = Server.MapPath("~/Content/myfile.xlsx");
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileLocation, contentType, fileName);
The second way, we can read the file from any location(database, server or azure storage) and send the file content to client side. For this way, please check whether the file has been read successfully. You can remote debug your azure web app to check whether the file content hasn't been read in the right way.
byte[] fileContent = GetFileContent();
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileContent, contentType, fileName);
5/27/2017 Update
Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.
How many instances does your Web App assigned? If your Web App have multi instances, the ExportTo request is handled by one instance and the Download request is handled by another instance. Since the TempData is store in memory of dedicated instance, it can't be got from another instance. According to the remote debug document. I find out the reason why the ExportTo action never gets called.
If you do have multiple web server instances, when you attach to the debugger you'll get a random instance, and you have no way to ensure that subsequent browser requests will go to that instance.
To solve this issue, I suggest you response the data directly from the ExportTo action or save the temp data in Azure blob storage which can't be accessed from multi instances.

ASP.Net MVC application access to other servers

We have an ASP.Net MVC application (website), just a straight web app. The URL it needs to connect to is an ASP.Net WebAPI2 web service on port 14015. The MVC application is calling the Web service anonymously using a WebClient class; the web service is secured by limiting which IPs can connect to it. There is no authorization mode to access except by IP.
using (WebClient client = new WebClient())
{
//****************************
// We make the web service call like this:
//****************************
string url = #"http://secure.example.com:14015/lms/SSOKey/1158341";
string key = client.DownloadString(url);
//****************************
// Then we append the returned key to build the full URL. This URL is used
// in the View to build a link button.
//****************************
string login_url = #"http://192.168.1.1/tc/login.do?uid=" + key;
login_url = login_url.Replace("\"", string.Empty);
//****************************
// Pass the URL to the view to build the link button
//****************************
ViewBag.LoginURL = login_url;
}
I can access the URL from a browser on the server where the MVC application is published, however, the call is unsuccessful. Any ideas how I may find out why this won't connect??
If you are calling the code inside of a controller and the users have to logon to your web app, than this code should give you what you need:
string currentUser = User.Identity.Name;
If your users use your web app anonymously currentUser will be blank.
Adding this code solved the problem.
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
Since we limit access by IP Addresses, very low security risk.

OAuth 1.0, javascript and server-to-server authentication

We are trying to integrate a "sign in with twitter" function in our application and for this purpose we are using javascript (hello.js).
Unfortunately, twitter is using oauth 1.0 so we can't have only a javascript solution but we need to implement a server-to-server communication to sign the request. The hello.js author provided an auth-server implementation for demo purposes based on node.js.
In our application for the backend part we are using java and I was wondering if a java solution exists for this purpose. Could I use for instance signpost or similar to do the job of auth-server ?
[UPDATE]
I tried to set the proxy used by hello.js to a mine servlet (so, instead of herokuap now I'm using a localhost servlet with oauth in the buildpath).
This servlet is doing the following:
OAuthConsumer consumer = new DefaultOAuthConsumer(
"xxxx",
"yyyyyyyyyyy");
OAuthProvider provider = new DefaultOAuthProvider(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
"https://api.twitter.com/oauth/authorize");
System.out.println("Fetching request token from Twitter...");
// we do not support callbacks, thus pass OOB
String authUrl = provider.retrieveRequestToken(consumer, "http://localhost:8080/oauth1/twitter/response_server");
URL url = new URL(authUrl);
HttpURLConnection req = (HttpURLConnection) url.openConnection();
req.setRequestMethod("GET");
req.connect();
BufferedReader rd = new BufferedReader(new InputStreamReader(req.getInputStream()));
StringBuilder d = new StringBuilder();
String line = null;
while ((line = rd.readLine()) != null){
d.append(line + '\n');
}
System.out.println(d);
PrintWriter out = response.getWriter();
out.println(d);
and it prints the twitter login page in the hello.js popup. In this way I got some encoding error but it is quite working.
Anyway the callback url is mapped to another servlet where I should simply "sign" the request but I think I am missing something because sometimes I got the error
"The server understand the request but it is still refusing it"
but if I close all browser window it works.
However the response servlet is similar to this one
OAuthConsumer consumer = new AbstractOAuthConsumer(
"xxxxx",
"yyyyyyyyyyy"){
#Override
protected HttpRequest wrap(Object arg0) {
return (HttpRequest)arg0;
}
};
consumer.sign(request);
But this code does not work because I don't know how to sign a tomcat request. In oauth homepage is explained how to sign jetty and apache common http request but not the tomcat one. However, is it correct my approach ?
Please checkout the spec of node-oauth-shim which is used my //auth-server and which HelloJS delivers.

Getting 401 Unauthorized from Google API Resumable Update

I am trying to integrate upload of arbitrary files to Google Docs into an existing application. This used to work before using resumable upload became mandatory. I am using Java client libraries.
The application is doing the upload in 2 steps:
- get the resourceId of the file
- upload the data
To get the resourceId I am uploading a 0-size file (i.e. Content-Length=0). I am passing ?convert=false in the resumable URL (i.e. https://docs.google.com/feeds/upload/create-session/default/private/full?convert=false).
I am passing "application/octet-stream" as content-type. This seems to work, though I do get different resourcesIds - "file:..." resourceIds for things like images, but "pdf:...." resourceIds for PDFs.
The second step constructs a URL based on the resourceId obtained previously and performs a search (getEntry). The URL is in the form of https://docs.google.com/feeds/default/private/full/file%3A.....
Once the entry is found the ResumableGDataFileUploader is used to update the content (0-byte file) with the actual data from the file being uploaded. This operation fails with 401 Unauthorized response when building ResumableGDataFileUploader instance.
I've tried with ?convert=false as well as ?new-revision=true and both of these at the same time. The result is the same.
The relevant piece of code:
MediaFileSource mediaFile = new MediaFileSource(
tempFile, "application/octet-stream");
final ResumableGDataFileUploader.Builder builder =
new ResumableGDataFileUploader.Builder(client, mediaFile, documentListEntry);
builder.executor(MoreExecutors.sameThreadExecutor());
builder.requestType(ResumableGDataFileUploader.RequestType.UPDATE);
// This is where it fails
final ResumableGDataFileUploader resumableGDataFileUploader = builder.build();
resumableGDataFileUploader.start();
return tempFile.length();
The "client" is an instance of DocsService, configured to use OAuth. It is used to find "documentListEntry" immediately before the given piece of code.
I had to explicitly specify request type, since it seems the client library code contains a bug causing NullPointerException for "update existing entry" case.
I have a suspicion that the issue is specifically in the sequence of actions (upload 0-byte file to get the resourceId, then update with actual file) but I can't figure out why it doesn't work.
Please help?
This code snippet works for me using OAuth 1.0 and OAuth 2.0:
static void uploadDocument(DocsService client) throws IOException, ServiceException,
InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(10);
File file = new File("<PATH/TO/FILE>");
String mimeType = DocumentListEntry.MediaType.fromFileName(file.getName()).getMimeType();
DocumentListEntry documentEntry = new DocumentListEntry();
documentEntry.setTitle(new PlainTextConstruct("<DOCUMENT TITLE>"));
int DEFAULT_CHUNK_SIZE = 2 * 512 * 1024;
ResumableGDataFileUploader.Builder builder =
new ResumableGDataFileUploader.Builder(
client,
new URL(
"https://docs.google.com/feeds/upload/create-session/default/private/full?convert=false"),
new MediaFileSource(file, mimeType), documentEntry).title(file.getName())
.requestType(RequestType.INSERT).chunkSize(DEFAULT_CHUNK_SIZE).executor(executor);
ResumableGDataFileUploader uploader = builder.build();
Future<ResponseMessage> msg = uploader.start();
while (!uploader.isDone()) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
throw ie; // rethrow
}
}
DocumentListEntry uploadedEntry = uploader.getResponse(DocumentListEntry.class);
// Print the document's ID.
System.out.println(uploadedEntry.getId());
System.out.println("Upload is done!");
}

Why doesn't my PDF document render/download in ASP.NET MVC2?

I have an ASP.NET MVC2 application in development and I am having problems rendering a .pdf file from our production server.
On my Visual Studio 2010 integrated development server everything works fine, but after I publish the application to the production server, it breaks. It does not throw any exceptions or errors of any kind, it simply does not show the file.
Here's my function for displaying the PDF document:
public static void PrintExt(byte[] FileToShow, String TempFileName,
String Extension)
{
String ReportPath = Path.GetTempFileName() + '.' + Extension;
BinaryWriter bwriter =
new BinaryWriter(System.IO.File.Open(ReportPath, FileMode.Create));
bwriter.Write(FileToShow);
bwriter.Close();
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = ReportPath;
p.StartInfo.UseShellExecute = true;
p.Start();
}
My production server is running Windows Server 2008 and IIS 7.
You cannot expect opening the default program associated with PDF file browsing on the server. Try returning the file into the response stream which will effectively open it on the client machine:
public ActionResult ShowPdf()
{
byte[] fileToShow = FetchPdfFile();
return File(fileToShow, "application/pdf", "report.pdf");
}
And now navigate to /somecontroller/showPdf. If you want the PDF opening inside the browser instead of showing the download dialog you may try adding the following to the controller action before returning:
Response.AddHeader("Content-Disposition", "attachment; filename=report.pdf");
i suggest you use ASP.NET MVC FileResult Class to display the PDF.
see http://msdn.microsoft.com/en-us/library/system.web.mvc.fileresult.aspx
your code open`s the PDF on the webserver.
Here's how I did it.
public ActionResult PrintPDF(byte[] FileToShow, String TempFileName, String Extension)
{
String ReportPath = Path.GetTempFileName() + '.' + Extension;
BinaryWriter bwriter = new BinaryWriter(System.IO.File.Open(ReportPath, FileMode.Create));
bwriter.Write(FileToShow);
bwriter.Close();
return base.File(FileToShow, "application/pdf");
}
Thank you all for your efforts. Solution I used is the most similar to the Darin's one (almost the same, but his is prettier :D), so I will accept his solution.
Vote up for all of you folks (both for answers and comments)
Thanks

Resources