Accessing additional querystring keys from ReturnUrl - forms-authentication

I need to access additional querystring parameters from the ReturnUrl querystring.
The entire site requires authentication so all pages will redirect to the login page if the user is not logged in or has timed out. So www.mysite.com/Default.aspx?id=H1234 will redirct to www.mysite.com/login.aspx?ReturnUrl=%2fDefault.aspx%3fid%3dH1234 and if the user then enters their credentials they will be returned to Default.aspx passing the id, but I need to check the id on the login page as well.
On the login page I can Server.UrlDecode(Request.RawUrl) to: /login.aspx?ReturnUrl=/Default.aspx?id=H1234 but as this has 2 question marks I now cannot access Request.QueryString("id")
I could use substring methods to extract the value but that worries me as the id is not a fixed length and it seems insecure.
There doesn't seem to be much on Google (or at least I'm not asking the right question)

No answers?
This is what I've managed to put together:
Private Function GetParam(ByVal paramName As String,
ByVal paramMaxLength As Integer) As String
Dim paramValue As String = String.Empty
Dim url As String = Server.UrlDecode(Request.RawUrl())
If Not url.Contains(paramName) Then
Return paramValue
End If
url = url.Substring(url.LastIndexOf("?") + 1)
ExtractParamValue(paramName, paramValue, url)
If paramValue.Length > paramMaxLength Then
paramValue = paramValue.Remove(paramMaxLength)
End If
Return paramValue
End Function
and
Private Shared Sub ExtractParamValue(ByVal paramName As String,
ByRef paramValue As String,
ByVal url As String)
For Each valuePair As String In url.Split("&")
If valuePair.StartsWith(paramName) Then
paramValue = valuePair.Substring(paramName.Length + 1)
End If
Next
End Sub

Related

WebClient.DownloadFile returns login page even when credentials are provided

I am trying to download a file that is behind a login page. I have looked into many ways to add credentials but the response is always the log in page's html. I have tried 2 passes with client.uploadValues or client.uploadstring to get a cookie and a single pass just providing credentials.
Public Sub downloadImage()
Dim username As String = "xxxxx"
Dim password As String = "xxxxx"
Using client As New WebClient()
client.Credentials = New NetworkCredential(username, password)
client.DownloadFile("https://retailerservices.domain.com/Image/ItemHighRes/26634/1", AppDomain.CurrentDomain.BaseDirectory & "test.jpg")
End Using
End Sub
Any recommendations?

How to manitain session values during HttpWebRequest?

In my code I'm sending a HttpWebRequest to a page in my website.
When request sends to this page, It doesn't maintain the Session values.
Below is the code, from where I'm generating the web request:
Public Overloads Shared Function ReadURL(ByVal sUrl As String) As String
Dim sBody As String
Dim oResponse As HttpWebResponse
Dim oRequest As HttpWebRequest
Dim oCookies As New CookieContainer
oRequest = WebRequest.Create("http://localhost:64802/inventory/purchase_order.aspx?id=5654")
oRequest.CookieContainer = oCookies
oResponse = oRequest.GetResponse()
Dim oReader As New StreamReader(oResponse.GetResponseStream())
sBody = oReader.ReadToEnd
oReader.Close()
oResponse.Close()
Return sBody
End Function
Below is the code written on Page_Load of Purchaseorder.aspx.vb:
iDomains_ID = Session("Domains_ID")
iLogin_ID = Session("Login_ID")
sPage = Request.Path
If Request.QueryString.Count > 0 Then sPage &= "?" & Request.QueryString.ToString()
sPage = shared01.Encrypt(sPage, Application("PK"))
If Not User.Identity.IsAuthenticated Or iLogin_ID = 0 Then
Response.Redirect("/login.aspx?page=" & sPage)
Exit Sub
End If
Above code doesn't gets the session values and it redirects to the login page.
So, how i can maintain the session on both pages during HttpWebRequest.
Looking for your replies.
EDIT
I've tried to use CookieContainer class as you can see in above code. But it doesn't work at all.
As an alternative, assuming the calling and called pages are in the same application, you could use the Server.Execute method to load the content of the page without making a separate request to the site:
Public Overloads Function ReadURL(ByVal sUrl As String) As String
Using writer As New StringWriter()
Server.Execute("~/inventory/purchase_order.aspx?id=5654", writer, False)
Return writer.ToString()
End Using
End Function
If I've understood you correctly, you're making a request from one page in your site to another, and you want to send the cookies from the current HttpRequest with your WebRequest?
In that case, you'll need to manually copy the cookies to the CookieContainer:
For Each key As String In Request.Cookies.AllKeys
Dim sourceCookie As HttpCookie = Request.Cookies(key)
Dim destCookie As New Cookie(sourceCookie.Name, sourceCookie.Value, sourceCookie.Path, "localhost")
destCookie.Expires = sourceCookie.Expires
destCookie.HttpOnly = sourceCookie.HttpOnly
destCookie.Secure = sourceCookie.Secure
oCookies.Add(destCookie)
Next
NB: You'll either need to make the ReadUrl function non-Shared, or pass the current HttpRequest as a parameter.
You'll also need to make sure the calling page has EnableSessionState="false" in the <%# Page ... %> directive, otherwise the page you're calling will hang trying to obtain the session lock.
Your code seems like you will need to make a request and a post. The first request will redirect you to your login page. The second will be a request where you post to the login page, which will start the session and (?) store information into the session variables. That post (to the login page) will then redirect you to the page you want.
I used code in this example http://www.codeproject.com/Articles/145122/Fetching-ASP-NET-authenticated-page-with-HTTPWebRe (I tweaked it a bit) to write an application to do this.

How to write response into Buffer instead of rendering on Screen?

I am developing app where I need to capture an Information from Webpage after giving credentials automatically. Some how I managed to do Automatic login and redirection of page. Here is my code :
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://abcd.com.au/categories/A_dfn/sdf");
HttpWebResponse res = req.GetResponse() as HttpWebResponse;
StringBuilder sb = new StringBuilder();
byte[] buf = new byte[10000];
Stream resStream = res.GetResponseStream();
string s = null;
int c = 0;
do
{
c = resStream.Read(buf, 0, buf.Length);
if (c != 0) {
s = ASCIIEncoding.ASCII.GetString(buf, 0, c);
sb.Append(s);
}
} while (c > 0);
string oldhead = "class=\"login_button\">";
string newhead = "class=\"login_button\"> <script type=\"text/javascript\">document.getElementById('btn').click()</script>";
sb.Replace(oldhead, newhead);
string oldbtn = "value=\"Submit\"";
string newbtn = "value=\"Submit\" id=\"btn\" ";
sb.Replace(oldbtn, newbtn);
string oldAction = "<form action=\"/login\" method=\"post\">";
string newAction = "<form action=\"https://abcd.com.au/login?orig_req_url=%2Fcategories/A_dfn/sdf\" method=\"post\">";
sb.Replace(oldAction, newAction);
string oldUsername = "<input id=\"login_email\" type=\"text\" name=\"user[email_address]\" class=\"textBox\" value=\"\">";
string newUserName = "<input id=\"login_email\" type=\"text\" name=\"user[email_address]\" class=\"textBox\" value=\"abc#xyz.com.au\">";
sb.Replace(oldUsername, newUserName);
string oldPass = "<input id=\"login_password\" type=\"password\" name=\"user[password]\" class=\"textBox\" value=\"\">";
string newPass = "<input id=\"login_password\" type=\"password\" name=\"user[password]\" class=\"textBox\" value=\"abc\">";
sb.Replace(oldPass,newPass);
Response.Write(sb);
This is show me expected output as I want by rendering page(Response.write(sb)). But, now I want to do same thing without redirecting to "https://abcd.com.au/login?orig_req_url=%2Fcategories/A_dfn/sdf" and want to do more stuff on this. I expect to get output of Response.Write(sb) in some buffer. Is it possible to do?
Here is example, that explains exactly what I want to do.
I am looking for an product's qty say name : Screw 15mm, this resides in page https://abcd.com.au/%2Fcategories/A_dfn/sdf.
So, I am requesting this url first, but as need login to access that page, its redirecting me to login page, filling username and password, pressing login button by using javascript,and then redirected to Originally Requested page. And on this page I want to find for that product, and return information to my web app.
All this I want to do without showing to user.
I just want to show retrieved information.
Thanks.
What you are looking for is a persisted session. Your approach towards this problem is incorrect. You are triggering the submit on the client-side. What you are trying to achieve should be done on the server-side.
The key to your scenario is to persist (store) the session & cookies set by the login page; then before your next request for the product info, inject the credential into the requesting webRequest.
Use the WebRequest object to load the login page.
Store any info (cookies) sent by the login page Response header.
create a new WebRequest object with the provided Response header, inject in userid/password.
Store any credentials returned by the Response.
Proceed to request for the quote info.
There is no generic way to do this without knowing the website you are trying to screen-scrap from. But the general step is as above. Basically, you need to create a custom class for this.
Also, you need HTMLAgilityPack to parse the HTML nodes. It is the correct method.
EDIT: Added my codes. Just so happen that I've created this class before sometime ago. So, you're in luck. However, you will need HTMLAgilityPack installed & referenced to use it. You can download HAP at: http://htmlagilitypack.codeplex.com/ If you want to do any serious screen-scraping, HAP is the de-facto standard.
Public Class clsBrowserSession
'=================================================================================================================================
'This is a special Browser Post class
' Instead of just POST to a URL as per the clsWeb.fnsPostResponse()
' clsBrowserSession allows us to LOAD a page first, persist all the cookies and variables, and then only POST to the target URL.
' The reason is that some program will drop (lets say) a SessionID as an input when you first load the page.
' and when you post, without the SessionID (variable), it will reject the POST. Thus clsBrowserSession can solve this problem.
'=================================================================================================================================
' USAGE:
' Dim voBrowserSession As New clsBrowserSession
' voBrowserSession.sbLoadPage("https://xxx.yyy.net.my/publicncdenq/index.htm")
' voBrowserSession.proFormElements("UserID") = "myID"
' voBrowserSession.proFormElements("Password") = "myPassword"
' Dim vsResponseHTML As String = voBrowserSession.Post("https://xxx.yyy.net.my/publicncdenq/index.htm")
Private vbIsPostingInProgress As Boolean
Public voCookies As System.Net.CookieCollection
Public proHTMLDoc As HtmlAgilityPack.HtmlDocument
Public proFormElements As clsFormElementCollection
Public Sub sbLoadPage(pvsURL As String)
vbIsPostingInProgress = False
fnoCreateWebRequestObject().Load(pvsURL)
End Sub
Public Function Post(pvsURL As String) As String
vbIsPostingInProgress = True
fnoCreateWebRequestObject().Load(pvsURL, "POST")
Return proHTMLDoc.DocumentNode.InnerHtml
End Function
Private Function fnoCreateWebRequestObject() As HtmlAgilityPack.HtmlWeb
Dim voWeb As New HtmlAgilityPack.HtmlWeb
voWeb.UseCookies = True
voWeb.PreRequest = New HtmlAgilityPack.HtmlWeb.PreRequestHandler(AddressOf event_OnPreRequest)
voWeb.PostResponse = New HtmlAgilityPack.HtmlWeb.PostResponseHandler(AddressOf event_OnAfterResponse)
voWeb.PreHandleDocument = New HtmlAgilityPack.HtmlWeb.PreHandleDocumentHandler(AddressOf event_OnPreHandleDocument)
Return voWeb
End Function
Private Sub sbAddPostDataTo(pvoRequest As Net.HttpWebRequest)
Dim vsPayload As String = proFormElements.fnsAssemblePostPayload()
Dim vabyteBuffer As Byte() = Text.Encoding.UTF8.GetBytes(vsPayload.ToCharArray())
pvoRequest.ContentLength = vabyteBuffer.Length
pvoRequest.ContentType = "application/x-www-form-urlencoded"
pvoRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11"
pvoRequest.GetRequestStream().Write(vabyteBuffer, 0, vabyteBuffer.Length)
End Sub
Private Sub sbAddvoCookiesTo(pvoRequest As Net.HttpWebRequest)
If (Not IsNothing(voCookies)) Then
If voCookies.Count > 0 Then pvoRequest.CookieContainer.Add(voCookies)
End If
End Sub
Private Sub sbSaveCookiesFrom(pvoResponse As Net.HttpWebResponse)
If pvoResponse.Cookies.Count > 0 Then
If IsNothing(voCookies) Then voCookies = New Net.CookieCollection
voCookies.Add(pvoResponse.Cookies)
End If
End Sub
Private Sub sbSaveHtmlDocument(pvoHTMLDocument As HtmlAgilityPack.HtmlDocument)
proHTMLDoc = pvoHTMLDocument
proFormElements = New clsFormElementCollection(proHTMLDoc)
End Sub
Protected Function event_OnPreRequest(pvoRequest As Net.HttpWebRequest) As Boolean
sbAddvoCookiesTo(pvoRequest)
If vbIsPostingInProgress Then sbAddPostDataTo(pvoRequest)
Return True
End Function
Protected Sub event_OnAfterResponse(pvoRequest As System.Net.HttpWebRequest, pvoResponse As Net.HttpWebResponse)
sbSaveCookiesFrom(pvoResponse)
End Sub
Protected Sub event_OnPreHandleDocument(pvoHTMLDocument As HtmlAgilityPack.HtmlDocument)
sbSaveHtmlDocument(pvoHTMLDocument)
End Sub
'-----------------------------------------------------------------------------------------------------
'Form Elements class
' Note: This element class will only capture (any) INPUT elements only, which should be enough
' for most cases. It can be easily modified to add other SELECT, TEXTAREA, etc voInputs
'-----------------------------------------------------------------------------------------------------
Public Class clsFormElementCollection
Inherits Dictionary(Of String, String)
Public Sub New(htmlDoc As HtmlAgilityPack.HtmlDocument)
Dim voInputs As Collections.Generic.IEnumerable(Of HtmlAgilityPack.HtmlNode) = htmlDoc.DocumentNode.Descendants("input")
For Each voInput As HtmlAgilityPack.HtmlNode In voInputs
Dim vsName = voInput.GetAttributeValue("name", "undefined")
Dim vsValue = voInput.GetAttributeValue("value", "")
If vsName <> "undefined" Then Add(vsName, vsValue)
Next
End Sub
Public Function fnsAssemblePostPayload() As String
Dim sb As New Text.StringBuilder
For Each voKeyValuePair In Me
Dim vsValue = System.Web.HttpUtility.UrlEncode(voKeyValuePair.Value)
sb.Append("&" & voKeyValuePair.Key & "=" & vsValue)
Next
Return sb.ToString.Substring(1)
End Function
End Class
End Class
Just make the above into a class object and instantiate it. The usage example is in the comment. You want the vsResponseHTML string.

Loading usercontrol to string and submitting the form within

What i'm doing is creating a website where the design is done i html files that are then read into the masterpage using System.IO.StreamReader.
and inside the html templates there are keywords like #USER.LOGIN#
that I replace with functions etc.
The issue is that i'm replacing #USER.LOGIN# With a usercontrol where there is a login form.
I have a function that reads the usercontrol into a string and it works.
but since the usercontrol is loaded to string alle the events are not following.
so when I submit the login form nothing nothing happends (the page posts) but cannot get any of the fields from the form...
NOTE:
i'm using url-rewriting so urls are http://www.domain.com/account/login
where account is account.aspx and login is the mode the account is in.
Code for replacing the keyword in the streamreader loop (pr line)
If InStr(line, "#USER.LOGIN#") Then
line = line.Replace("#USER.LOGIN#", vbCrLf & userfunc.GetMyUserControlHtml("uc", "account_login.ascx", "/account/login/") & vbCrLf)
End If
And the functions to read usercontrol
Public Shared Function GetMyUserControlHtml(contextKey As String, controllerfile As String, Optional ByVal formaction As String = "")
Dim myId As Guid = New Guid()
Return userfunc.RenderUserControl("~\Controllers\" & controllerfile, "", myId, formaction)
End Function
Public Shared Function RenderUserControl2(path As String, Optional ByVal formaction As String = "") As String
Using pageHolder As New Page(), _
viewControl As UserControl = DirectCast(pageHolder.LoadControl(path), UserControl), _
output As New StringWriter(), _
tempForm As New HtmlForm()
If formaction <> "" Then
tempForm.Action = formaction
Else
tempForm.Action = HttpContext.Current.Request.RawUrl
End If
tempForm.Controls.Add(viewControl)
pageHolder.Controls.Add(tempForm)
HttpContext.Current.Server.Execute(pageHolder, output, False)
Dim outputToReturn As String = output.ToString()
Return outputToReturn
End Using
End Function
How would you guyz do this?
I need the userlogin to be hardcoded in the usercontrol but still be able to place it anywhere using the template keyword.
This will also be used with other functions (newsletter signup, shoutbox etc.)
what i would suggest is register you control on the web config..
<add tagPrefix="CustomControl" tagName="LogIn" src="~/UserControls/Login.ascx"/>
you can still use "#USER.LOGIN#" but instead of replacing it with a control...
replace it with a something like this
<CustomControl:LogIn id="LogIn" runat="server"/>
this is just a quick write up.. but you could always try if it works
you can save your HTML like this istead of placing an actual "#USER.LOGIN#"
<% =GetLoginControl() %>
and then create a public function in your code behind named GetLoginControl() and return a response.write of the HTML Mark up you need

ASP.NET - Populate a page with input entered from a previous page

on my website I have an "Enroll" page where users submit some basic information, there's some basic validation on that page then the user is taken to an "Enrollment Confirmed" page (which i don't want to display the info from previous page). On the confirmation page there is link to a "Print Enrollment Confirmation" page which, on this page, I want to contain the information entered from the "Enroll" page. So basically, I want the input entered on "Page 1" put into labels on "Page 3". I've seen some examples of transferring information from "Page 1" to "Page 2" but I have an extra page users need to go through before hitting the page with their previously entered data.
Can someone give me an explanation on how I could do this without using query strings? Thank you.
You could create an class with properties for each form field then store it in the session. Then after you populate what you need on page 3 remove it from the session.
Example
Class:
<Serializable()>
Public Class Input
Private _FirstName As String = String.Empty
Private _LastName As String = String.Empty
Public Property FirstName As String
Get
Return _FirstName
End Get
Set(ByVal value As String)
_FirstName = value
End Set
End Property
Public Property LastName As String
Get
Return _FirstName
End Get
Set(ByVal value As String)
_FirstName = value
End Set
End Property
Public Sub New()
End Sub
End Class
Storing data:
Private Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSubmit.Click
Dim FormData As New Input()
FormData.FirstName = txtFirstName.Text
FormData.LastName = txtLastName.Text
Session("InputData") = FormData
End Sub
Retrieving it:
If Not IsNothing(Session("InputData")) Then
Dim FormData As Input = DirectCast(Session("InputData"), Input)
txtFirstName.Text = FormData.FirstName
txtLastName.Text = FormData.LastName
Session.Remove("InputData")
End If
You could use the button.postbackurl property to post the data to another page:
http://msdn.microsoft.com/en-us/library/ms178140.aspx
In the intermediary pages, you could store the data in hidden fields from page 1, so the data would be in the posted results for page 3, when another button posts the data from page 2 to page 3.
HTH.
This pretty much sums up your choices:
http://msdn.microsoft.com/en-us/library/6c3yckfw.aspx
I would store the values in hidden input fields on page 2 and if page 3 is called as a form submit, then the values will be available through Request.Form.

Resources