pass authentication header to image requests in iframe - asp.net

I am sure that title of my question doesn't makes much sense, but i couldn't think better now.
Problem: My main task is to show all pages of a SSRS report in a popup inside one of the pages of a ASP.NET MVC application.
To achieve this I used below approach:
Add a jQuery popup in MyPage.cshtml(i need report contents inside this popup)
When this popup opens(on some client action), I make a jquery ajax request to second page proxyPage.aspx
On proxy page I make a webrequest to reportserver with network credentials and get report html
WebRequest request = WebRequest.Create( "http://MyReportServer/ReportServer?/ MyReportName&rs:Command=Render&rs:Format =HTML4.0&rc:Toolbar=false&Param1=blabla123");
request.Credentials = new NetworkCredential(MYUSERNAME, MYPASSWORD);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream receiveStream = response.GetResponseStream();
StreamReader readStream = new StreamReader(receiveStream, System.Text.Encoding.UTF8);
string str = readStream.ReadToEnd();
Response.Write(str);
HTML from proxyPage I write in a div inside popup or using iframe to show full proxy page inside it.
Till here things go well and thereafter I get yet another problem for which I am writing this question
When report's HTML gets rendered in popup it makes request to report server to retrieve images embedded in report.
Since these requests to report server doesn't send network credentials as I did in step 3, I get prompt for entering credentials.
I need a approach through which these image request may somehow get authenticated by credentials that I have already supplied earlier.

SSRS will stream the resources in a location you specify.
private byte[] InternalRenderReport(Report report, List<ReportParameter> parameters, string format, int pageNumber, ref int totalPages, string virtualTempFolder, string physicalTempFolder)
{
CheckConnection();
byte[] result = null;
ReportExecution2005.ReportExecutionService _execService=new ReportExecution2005.ReportExecutionService();
sys = _systemService.GetCurrentSystem();
_execService.Url = sys.ReportingServices.ServiceRootURL+"/ReportExecution2005.asmx";
NetworkCredential credentials = new NetworkCredential(sys.ReportingServices.Credentials.UserName,
sys.ReportingServices.Credentials.Password,
sys.ReportingServices.Credentials.Domain);
_execService.Credentials=credentials;
ReportExecution2005.ParameterValue[] rsParams = null;
if (parameters != null)
{
rsParams = new ReportExecution2005.ParameterValue[parameters.Count];
int x = 0;
foreach (ReportParameter p in parameters)
{
rsParams[x] = new ReportExecution2005.ParameterValue();
rsParams[x].Name = p.ParameterName;
rsParams[x].Value = p.SelectedValue;
x++;
}
}
StringBuilder devInfo = new StringBuilder();
if (format.ToUpper().StartsWith("HTML"))
{
devInfo.Append("<DeviceInfo>");
devInfo.Append("<HTMLFragment>True</HTMLFragment>");
devInfo.Append("<Section>" + pageNumber.ToString() +"</Section>");
devInfo.Append("<StreamRoot>" + virtualTempFolder + "</StreamRoot>");
/*devInfo.Append("<Zoom>200</Zoom>");*/
devInfo.Append("</DeviceInfo>");
}
else
devInfo.Append("<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>");
string extension;
string mimeType;
string encoding;
string[] streamIDs = null;
ReportExecution2005.Warning[] warnings = null;
ReportExecution2005.ExecutionHeader execHeader = new ReportExecution2005.ExecutionHeader();
ReportExecution2005.ExecutionInfo rpt = _execService.LoadReport(report.ReportPath, null);
if(rsParams!=null)
_execService.SetExecutionParameters(rsParams, "en-us");
_execService.ExecutionHeaderValue = execHeader;
_execService.ExecutionHeaderValue.ExecutionID = rpt.ExecutionID;
//result = _execService.Render2(format, devInfo, ReportExecution2005.PageCountMode.Actual, out extension, out mimeType, out encoding, out warnings, streamIDs);
result = _execService.Render(format, devInfo.ToString(), out extension, out mimeType, out encoding, out warnings, out streamIDs);
if (format.ToUpper().StartsWith("HTML"))
{
// For each image stream returned by the call to render,
// render the stream and save it to the application root
string FilePath = physicalTempFolder;
byte[] image;
// For each image stream returned by the call to render,
// render the stream and save it to the application root
foreach (string streamID in streamIDs)
{
image = _execService.RenderStream("HTML4.0", streamID, null, out encoding, out mimeType);
FileStream stream = File.OpenWrite(FilePath + streamID);
stream.Write(image, 0, image.Length);
stream.Close();
}
}
rpt = _execService.GetExecutionInfo();
totalPages = rpt.NumPages;
return result;
}
This will return either raw HTML or the contents to push a file. I added a Temp folder to the solution to be deployed to the server. you can place a web.config file in the temp folder with the following contents to allow extension less contents as ssrs will render when using streams:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".*" mimeType="image/png" />
</staticContent>
<handlers>
<clear />
<add name="StaticFile" path="*" verb="*" type="" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" scriptProcessor="" resourceType="Either" requireAccess="Read" allowPathInfo="false" preCondition="" responseBufferLimit="4194304" />
</handlers>
</system.webServer>
</configuration>
Then use the following to functions to get the physical and Virtual Temp folders:
PhyscicalTempFolder= AppDomain.CurrentDomain.BaseDirectory + #"Temp\";
VirtualTempFolder=return Url.Content("~/Temp/");
And finally to clean up after each day you can add a powershell command similar to:
Remove-Item D:\xxx\WebApplications\ExternalReports\Temp\* -exclude *.config
Then add a .bat that calls the PS script:
powershell -command "& 'C:\xxx\Scripts\SSRSCleanTempFiles\SSRSCleanTempFiles.ps1'"
With this you can configure a scheduled task on the server to call the .bat file everyday and clean the temp folder of your application.

Related

Has anyone used Sage Pay with asp.net Webpages

Has anyone used Sage Pay with asp.net Webpages??
I have downloaded the integration kit from Sage Pay but this is made in webforms and i am having trouble converting it the WebPages format.
Sage Pay are no help so i'm hoping someone out there has done this. Or can point me in the right direction.
I have managed to do this using the class from the SagePay template you can download.
Put these files in your bin folder:-
SagePay.IntegrationKit.DotNet.dll
SagePay.IntegrationKit.DotNet.pdb
Put these files in your App_Code folder:-
SagePayConfiguration.cs
SagePayAPIIntegration.cs
SagePayFormIntegration.cs
You also need to add some stuff to your web.config file
<SagePayConfiguration>
<!--Mandatory
Set to TEST for the Test Server and LIVE for the live environment-->
<add key="sagepay.api.env" value="TEST" />
<!--Transaction Settings -->
<add key="sagepay.api.protocolVersion" value="3.00" />
<add key="sagepay.kit.vendorName" value="your Name" />
<add key="sagepay.kit.fullUrl" value="your url" />
<add key="sagepay.kit.currency" value="GBP" />
<!--Optional setting. It's recommended to set the siteFqdn value to the Fully
Qualified Domain Name of your server.
This should start http:// or https:// and should be the name by which our servers can call back to yours
i.e. it MUST be resolvable externally, and have access granted to the Sage Pay servers
examples would be https://yoursite or http://212.111.32.22/
NOTE: Do not include any URI path.
If you leave this value blank the kit will use the current host name-->
<add key="sagepay.kit.siteFqdn.LIVE" value="http://your web address" />
<add key="sagepay.kit.siteFqdn.TEST" value="http://your web address" />
<!--Mandatory. Usually PAYMENT. This can be DEFERRED or AUTHENTICATE if your Sage Pay
account supports those payment types
NB Ideally all DEFERRED transaction should be released within 6 days (according to card scheme rules).
DEFERRED transactions can be ABORTed before a RELEASE if necessary-->
<add key="sagepay.kit.defaultTransactionType" value="PAYMENT" />
<!--0 = If AVS/CV2 enabled then check them. If rules apply, use rules (default).
1 = Force AVS/CV2 checks even if not enabled for the account. If rules apply, use rules.
2 = Force NO AVS/CV2 checks even if enabled on account.
3 = Force AVS/CV2 checks even if not enabled for the account but DON'T apply any rules.-->
<add key="sagepay.kit.applyAvsCv2" value="0" />
<!--0 = If 3D-Secure checks are possible and rules allow, perform the checks and apply the authorisation rules. (default)
1 = Force 3D-Secure checks for this transaction if possible and apply rules for authorisation.
2 = Do not perform 3D-Secure checks for this transaction and always authorise.
3 = Force 3D-Secure checks for this transaction if possible but ALWAYS obtain an auth code, irrespective of rule base.-->
<add key="sagepay.kit.apply3dSecure" value="0" />
<!--FORM Protocol Only Settings
Set this value to the Encryption password assigned to you by Sage Pay -->
<add key="sagepay.kit.form.encryptionPassword.TEST" value="Your password" />
<add key="sagepay.kit.form.encryptionPassword.LIVE" value="Your password" />
<!--The Sage Pay server URLs to which customers will be sent for payment for each environment-->
<add key="sagepay.api.formPaymentUrl.LIVE" value="https://live.sagepay.com/gateway/service/vspform-register.vsp" />
<add key="sagepay.api.formPaymentUrl.TEST" value="https://test.sagepay.com/gateway/service/vspform-register.vsp" />
</SagePayConfiguration>
To make this easier to manage i have but this web.config file in the checkout folder so it is easy to keep updated.
I also created the following class to encrypt and dencrypt the data:-
you can call it what ever you want but it needs to be saved in the App_Code folder.
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.IO;
public static class EncryptionHelper
{
private static byte[] keyAndIvBytes;
static EncryptionHelper()
{
// You'll need a more secure way of storing this, I this isn't
// a real key
keyAndIvBytes = UTF8Encoding.UTF8.GetBytes("123123123123123b");
}
public static string ByteArrayToHexString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static string DecodeAndDecrypt(string cipherText)
{
string DecodeAndDecrypt = AesDecrypt(StringToByteArray(cipherText));
return (DecodeAndDecrypt);
}
public static string EncryptAndEncode(string plaintext)
{
return ByteArrayToHexString(AesEncrypt(plaintext));
}
public static string AesDecrypt(Byte[] inputBytes)
{
Byte[] outputBytes = inputBytes;
string plaintext = string.Empty;
using (MemoryStream memoryStream = new MemoryStream(outputBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(cryptoStream))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
return plaintext;
}
public static byte[] AesEncrypt(string inputText)
{
byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText);//AbHLlc5uLone0D1q
byte[] result = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(keyAndIvBytes, keyAndIvBytes), CryptoStreamMode.Write))
{
cryptoStream.Write(inputBytes, 0, inputBytes.Length);
cryptoStream.FlushFinalBlock();
result = memoryStream.ToArray();
}
}
return result;
}
private static RijndaelManaged GetCryptoAlgorithm()
{
RijndaelManaged algorithm = new RijndaelManaged();
//set the mode, padding and block size
algorithm.Padding = PaddingMode.PKCS7;
algorithm.Mode = CipherMode.CBC;
algorithm.KeySize = 128;
algorithm.BlockSize = 128;
return algorithm;
}
}
I call the the class like so:-
string crypt = "blahblahblah";
string EncryptAndEncode = EncryptionHelper.EncryptAndEncode(crypt);
string DecodeAndDecrypt = EncryptionHelper.DecodeAndDecrypt(EncryptAndEncode);
When the transaction is complete i get the crypt with this code:-
IFormPaymentResult PaymentStatusResult = new DataObject();
if (Request.QueryString["crypt"] != null && !string.IsNullOrEmpty(Request.QueryString["crypt"]))
{
SagePayFormIntegration sagePayFormIntegration = new SagePayFormIntegration();
PaymentStatusResult = sagePayFormIntegration.ProcessResult(Request.QueryString["crypt"]);
}
you can then call the needed information from the calss like so
if (PaymentStatusResult.Status == ResponseStatus.NOTAUTHED)
{reason = "You payment was declined by the bank. This could be due to insufficient funds, or incorrect card details.";}
You can see all the fields in the Result.aspx of the SagePay template.

Get FileSize in Server is always zero

I'm doing a file upload function in my ASP.NET MVC web system. The file upload function is working, so the next step I do is to validate the file size.
Please see the attached codes
Partial form GEDocumentInfoForm.ascx:
<input type="file" name = "Files" class = "multi" id = "myFile"/>
Main Form Create.aspx
<asp:Content ID="Content4" ContentPlaceHolderID="ContentCph" runat="server">
<script type="text/javascript">
$(document).on('click', '#btnCreateDocument', function () {
$('#btnCreateDocument').attr('disabled', 'disabled'); // prevent resubmit
Checksize()
document.body.style.cursor = 'wait';
});
function Checksize() {
alert(document.getElementById("myFile").tagName);
var k = document.getElementById("myFile").files[0].size;
alert("file size in KB " + k / 1024);
}
</script>
<% Using (Html.BeginForm("Create", "GEDocument", FormMethod.Post, New With {.enctype = "multipart/form-data", .id = "form"}))%>
<input type="submit" name="Save" value="<%= Detail.Save %>" id="btnCreateDocument" />
<div id="Div1">
<% Html.RenderPartial("GEDocumentInfoForm", Model) %>
</div>
<% End Using%>
</asp:Content>
The file size validation (not more than 2048B) was working fine in localhost. So, after that I published it and deploy in my development server. When I run it, somehow it can pass through my validation. After check in debug mode of web browser, it returns 0 for the file size.
var k = document.getElementById("myFile").files[0].size;
I've tried to search solutions to see if anyone hit the similar issue before. End up, I have to use server validation in my Controller.
Dim fileZs As HttpFileCollectionBase = Request.Files
For z As Integer = 0 To (fileZs.Count - 1)
Dim file As HttpPostedFileBase = fileZs(z)
If Not IsNothing(file) AndAlso (file.ContentLength / 1024) > 2048 Then
errors.Concat(New RuleViolation(Message.EmailMustHaveValue, "SelectedToEmails"))
End If
Next
Web.Config (added the configuration so that it can pass ActionFilterAttribute in Controller due to Maximum request too long)
<system.web>
<httpRuntime maxRequestLength="1048576" />
</system.web>
I think that server validation is not user-friendly. I wish there are some answers from the experts if anyone faced the issue like me in doing Client validation to check file size in file upload feature.
Why is it always return 0 after published to development server?
Is it related to server security? As I know we are getting FileName as C:\fakePath\myFileName. Could it be some relationship over here?
here a full working example, note Request.Files is an array, if you are sending only one file you need to pick first item.
the right property to check is ContentLength
also check if in your folder the uploaded file exists after upload, because you need to have write permission in the folder where you are uploading
[HttpPost]
public ActionResult Upload()
{
if (Request.Files.Count > 0)
{
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Images/"), fileName);
file.SaveAs(path);
}
}
//............
}

Cannot upload large (>50MB) files to SharePoint 2010 document library

I'm trying to upload a large file to a document library, but it fails after just a few seconds. The upload single document fails silently, upload multiple just shows a failed message. I've turned up the file size limit on the web application to 500MB, and the IIS request length to the same (from this blog), and increased the IIS timeout for good measure. Are there any other size caps that I've missed?
Update I've tried a few files of various sizes, anything 50MB or over fails, so I assume something somewhere is still set to the webapp default.
Update 2 Just tried uploading using the following powershell:
$web = Get-SPWeb http://{site address}
$folder = $web.GetFolder("Site Documents")
$file = Get-Item "C:\mydoc.txt" // ~ 150MB
$folder.Files.Add("SiteDocuments/mydoc.txt", $file.OpenRead(), $false)
and get this exception:
Exception calling "Add" with "3" argument(s): "<nativehr>0x80070003</nativehr><nativestack></nativestack>There is no file with URL 'http://{site address}/SiteDocuments/mydoc.txt' in this Web."
which strikes me as odd as of course the file wouldn't exist until it's been uploaded? N.B. while the document library has the name Site Documents, it has the URL SiteDocuments. Not sure why...
Are you sure you updated the right webapp? Is the filetype blocked by the server? Is there adequate space in your content database? I would check ULS logs after that and see if there is another error since it seems you hit the 3 spots you would need too update.
for uploading a large file, you can use the PUT method instead of using the other ways to upload a document.
by using a put method you will save the file into content database directly. see the example below
Note: the disadvantage of the code below is you cannot catch the object that is responsible for uploading directly, on other word, you cannot update the additional custom properties of the uploaded document directly.
public static bool UploadFileToDocumentLibrary(string sourceFilePath, string targetDocumentLibraryPath)
{
//Flag to indicate whether file was uploaded successfuly or not
bool isUploaded = true;
try
{
// Create a PUT Web request to upload the file.
WebRequest request = WebRequest.Create(targetDocumentLibraryPath);
//Set credentials of the current security context
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = “PUT”;
// Create buffer to transfer file
byte[] fileBuffer = new byte[1024];
// Write the contents of the local file to the request stream.
using (Stream stream = request.GetRequestStream())
{
//Load the content from local file to stream
using (FileStream fsWorkbook = File.Open(sourceFilePath, FileMode.Open, FileAccess.Read))
{
//Get the start point
int startBuffer = fsWorkbook.Read(fileBuffer, 0, fileBuffer.Length);
for (int i = startBuffer; i > 0; i = fsWorkbook.Read(fileBuffer, 0, fileBuffer.Length))
{
stream.Write(fileBuffer, 0, i);
}
}
}
// Perform the PUT request
WebResponse response = request.GetResponse();
//Close response
response.Close();
}
catch (Exception ex)
{
//Set the flag to indiacte failure in uploading
isUploaded = false;
}
//Return the final upload status
return isUploaded;
}
and here are an example of calling this method
UploadFileToDocumentLibrary(#”C:\test.txt”, #”http://home-vs/Shared Documents/textfile.pdf”);

asp.net fileupload control time-out on big files

Getting ERR_CONNECTION_RESET after more or less 2 minutes of uploading a rather big file (90 MB) through asp.net fileupload control.
Testing this on a shared host environment so I don't know exactly what policies are enforced on me.
web.config settings are sufficient I think:
<httpRuntime requestValidationMode="2.0" maxRequestLength="1000000" executionTimeout="45000" />
Should I be looking at other async file-upload controls? Am I missing a web.config setting? Is the upload control simply not sufficient for large files on slow connections?
There can be a number of reasons why the connection is reset and upping the max request length works to a point but you are right about looking into async file uploaders. The most important part is using one which "chunks" the files into smaller pieces and so avoids request limits etc. I have had the best experience with plupload:
http://www.plupload.com/
Here is some code for receiving the files (this is MVC but you can refactor to use a handler in .NET classic):
[HttpPost]
public ActionResult UploadImage(int? chunk, int? chunks, string name)
{
var fileData = Request.Files[0];
if (fileData != null && fileData.ContentLength > 0)
{
var path = GetTempImagePath(name);
fileSystem.EnsureDirectoryExistsForFile(path);
// Create or append the current chunk of file.
using (var fs = new FileStream(path, chunk == 0 ? FileMode.Create : FileMode.Append))
{
var buffer = new byte[fileData.InputStream.Length];
fileData.InputStream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
}
return Content("Chunk uploaded", "text/plain");
}

How do I format a URI when binding an Image in Silverlight?

I haven't been able to find an answer to this.
I have a database that has image paths in it ("images/myimage.jpg"). These images exist on my asp.net site which is also where I host the SL. I want to bind these images to my ListBox control so that the image displays.
I have read that since I have a string value, "images/myimage.jpg", that I need to convert it to a BitMap image. I have done this:
The xaml:
<Image Source="{Binding ImageFile, Converter={StaticResource ImageConverter}}"/>
The ImageConverter class:
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
Uri source= new Uri(value.ToString());
return new BitmapImage(source);
}
catch(Exception ex)
{
return new BitmapImage();
}
}
I get an error when creating the URI, "The Format of the URI could not be determined". What am I doing wrong? If I create a Uri that looks like this: http://localhost:49723/images/myimage.jpg, it works just fine.
Why doesn't just "images/myimage.jpg" work?
A simple, dynamic approach that will work regardless of where your XAP file is located is similar to the following.
//Get the root path for the XAP
string src = Application.Current.Host.Source.ToString();
//Get the application root, where 'ClientBin' is the known dir where the XAP is
string appRoot = src.Substring(0,src.IndexOf("ClientBin"));
//Create the image / uri
BitmapImage img = new BitmapImage();
img.UriSource = new Uri(appRoot + "/Images/myImage.png", UriKind.Relative);
Does this help?
Relative paths to media in Silverlight are wacky so they can work the same (wacky) way that WPF paths do. Relative paths are relative to the XAP file, not the app root.
One trick is to move your XAP to the root of your website, so media paths will be relative to the root.
See my post on relative URI's in Silverlight here.
Just ran into this problem today myself and fixed it the way Jon describes above (without seeing your post though. Could have saved me some time.) I'd also point out that the specific error can be resolved by using the overload:
Uri source = new Uri("Path/Image.jpg", UriKind.Relative);
You'd still be unable to access the images subdirectory without moving the XAP file, but it resolves the error message. At that point the program just happily returns an image with no content, leaving you to use Fiddler or Web Dev Helper to figure out the real problem.
http://www.silverlightexamples.net/post/How-to-Get-Files-From-Resources-in-Silverlight-20.aspx
Resource, and Never Copy on the image, then use "SLapplicationName;component/mypathtoimage/image.png"
using System.Windows.Resources; // StreamResourceInfo
using System.Windows.Media.Imaging; // BitmapImage
....
StreamResourceInfo sr = Application.GetResourceStream(
new Uri("SilverlightApplication1;component/MyImage.png", UriKind.Relative));
BitmapImage bmp = new BitmapImage();
bmp.SetSource(sr.Stream);
You can simply write a method that gives you full server address (protocol://server:port/) and use it to create absolute URLs:
public class Helper{
public static string ServerAddress{
get{
if (_server == "")
_server = _ServerAddress();
return _server;
}
}
private static string _ServerAddress(){
HttpContext ctx = HttpContext.Current;
if (ctx == null) return "";
HttpRequest request = ctx.Request;
if (request == null) return "";
string srvr = request.ServerVariables["SERVER_NAME"];
string port = string.IsNullOrEmpty(request.ServerVariables["SERVER_PORT"])?
"" : ":" + request.ServerVariables["SERVER_PORT"];
string protocol = "http://";//request.ServerVariables["SERVER_PROTOCOL"];
return string.Format("{0}{1}{2}{3}", protocol, srvr, port,
request.ApplicationPath);
}
}
and change you Converter method line:
Uri source= new Uri(value.ToString());
to
if(!string.IsNullOrEmpty(value.ToString()))
Uri source= new Uri(Helper.WebAddress + value.ToString());

Resources