Referencing other aspx controls events in a module - asp.net

For an ASP.Net application using VB how can I reference an event or textbox not located within the same file.
For example when you are coding on say Default.aspx and you put a textbox on the page this works to reference it.
Dim username As String
username = Textbox1.Text
Ok but now I want to get the values and response and process it (amongst other tasks) in a separate module say security.vb.
how can I effectively call it from security.vb so it says username = "Default.aspx".Textbox1.Text
i have tried many versions to achieve this and Google'd but I don't know the correct terms to search so am not getting a good result.
The closest Stack question is Reference from Module but that doesn't have an answer. I know this must be so simple but it eludes me.

Another "module"? You're not using classes? Using classes would make this easy:
In Default.aspx.vb
username = Textbox1.Text
Dim security As New Security(username)
In Security.vb:
Public Sub New(ByVal username as String)
Me.username = username
End Sub
Private username as String
Then you can access the username variable in your Security class whenever you need it. (Note that since the username variable is not declared as Shared, it will only be valid for the current instance of the Security class that you created in Default.aspx.vb. You could make it Shared, but that would be a bad idea on a web server, since if you did, that would mean that only one user could be logged in at a time, and whenever Bob logs in, Alice's session suddenly starts displaying Bob's data!)

Related

Is it safe to use a Shared property for authentication verification?

Should I be using Shared, or just a Public function and initiate a class when needed? I have read a number of articles, but still cant get my head around the best option here.
Here is the code I want to get. The userid in question is a string that's set when the user logs in. As I may need the userid in a number of pages, I want to add it in a class.
Public Shared ReadOnly Property userid As String
Get
Dim ck As HttpCookie = HttpContext.Current.Request.Cookies(FormsAuthentication.FormsCookieName)
Dim tkt As FormsAuthenticationTicket = FormsAuthentication.Decrypt(ck.Value)
Return tkt.Name
End Get
End Property
I would almost never say that you should leverage a Shared member when dealing with authentication - but since you're using Cookies I can't see any reason this code wouldn't work as is and make it easier to access.
It is perfectly fine and acceptable. You can see accessing HttpContext.Current inside static method in a lot of open source projects.
FYI: HttpContext.Current must be on current calling thread, so make sure to check HttpContext.Current is not null.

Aspx, global instance of class, possible bug in code structure

I am tracking down a bug in some old aspx code. The problem is that one some very rare occations (1/10.000 pageviews or so) two users are mixed up, ie. user A sees user B data.
Here is how the code is structured: We have a user class which is defined in a module like this:
Public Module MyGlobals
Public myUser As CMyUser
End Module
On the loginpage, we validate the username/password and if valid then the coorosponding userid is loaded from db, and we do:
FormsAuthentication.SetAuthCookie(userid, False)
Then we redirect to the secure area. In the secure areas MasterPage, on event Page_Init, we then have:
If Context.User.Identity.IsAuthenticated then
' Initialize the user class (user data is loaded)
MyGlobals.myUser = New CMyUser(Context.User.Identity.Name)
Else
' Redirect to loginpage
End If
Hereafter, is it safe to access the
MyGlobals.myUser
instance from every page which has the secure masterpage as masterpage, or could there be issues with this structure?
A VB.Net Module is like a static class with a private constructor and only static fields in C#.
That means, all variables declared in a module are shared across all threads. Hence every request(User) that's using this module will overwrite the old value.
I would strongly recommend to use Session to store user-sensitive data.
But i'm not sure why you want to store the Username because it's already stored when using FormsAuthentication(as you've shown yourself above).
If you really need this wrapper, you could easily achieve it even in a static context via HttpContext.Current.Session:
Module MyGlobals
Public Property myUser As CMyUser
Get
If HttpContext.Current.Session("CurrentUser") Is Nothing Then
Return Nothing
Else
Return DirectCast(HttpContext.Current.Session("CurrentUser"), CMyUser)
End If
End Get
Set(ByVal value As CMyUser)
HttpContext.Current.Session("CurrentUser") = value
End Set
End Property
End Module

Where should i store "MemberID"?

In my webpage i use FormsAuthentication
FormsAuthentication.RedirectFromLoginPage(VisitorEmail, False)
Every time the visitor gets authenticated via the login page, i set the
Session("MemberID") = GetMemberIDByEmail(VisitorEmail) for later processing.
Since i need both MemberID and VisitorEmail.
But something tells me that this is "out of the book" and not "by the book".
So am i doing something WRONG or BAD here?
Sorry, I'm not sure exactly what you are trying to do from your description, but there's no need to store the MemberID in session state. Whenever you need it, just call:
Membership.GetUser.ProviderUserKey
Note: Its not really considered good form to store information in Session state as this could be lost e.g. if the web server resets - which it does periodically, or if the site needs to recompile. Also, its not very scalable as each "active" user will use up memory and also if you ever need to move to a web farm session state can cause issues as it will be different on each web server.
Prob OK for a little, quick site though ;-)
It's fine to use Session to cache this type of info, but remember to reassign it when the session expires in Global.asax:
void Session_Start(object sender, EventArgs e)
{
if(Request.IsAuthenticated) //to make sure the user has not logged out
Session["MemberID"] = GetMemberIDByEmail(VisitorEmail);
}
You could create a custom principal class so you can add the additional properties. Then modify your Global.asax to override Application_PostAuthenticateRequest with your code and also set Context.User = Thread.CurrentPrincipal = myPrincipal;. Best is to always set Thread.CurrentPrincipal, but normally you can also get to your own properties elsewhere in your code using the more "convenient" Page.User or Context.User.
Context.User vs. Thread.CurrentPrincipal / why FormsAuthentication can be subtle
Set custom IIdentity or IPrincipal / Store user id in Principal or Identity?
Could you not switch the two around and store the member id in the form variable (since I assume the user is able to change there email address and not there member id)...
Dim memberId as Integer = GetMemberIDByEmail(VisitorEmail)
' assuming integer here and that a result is found etc etc
' set the form authentication stuff
FormsAuthentication.RedirectFromLoginPage(memberId, False)
And then you can always look up the email address from the memberId (caching it perhaps against the member id across requests)
Public Function GetMemberEmail(Byval memberId as Integer) As String
Dim cacheKey as String = "member-email-" & memberId
Dim email as String
If Cache.Item(cacheKey) is Nothing Then
email = GetMemberEmailByID(memberId)
Cache.Insert(cacheKey, email ...
Else
email = Cache.Item(cacheKey)
End If
return email
End Function
If you need both pieces of information, and the Id is less likely to change, it would seem the better value to be used for your forms authentication....and you can always look up the email address from the value.

Sql data reader in classes

Hi
On several pages of our website, I want to check if the currently logged in user has accepted our terms and conditions in the past. This boolean value is stored in the application database. Rather than creating a sql data reader afresh on each relevant page, I thought I could put it in a class and then assign the true/false to a variable. This is what I have so far and it does exactly what I want it to:
Public Shared ReadOnly Property termsCompleted As String
Get
Dim selectTerms As String = "SELECT Terms FROM tblPersonal WHERE Ref=#Ref"
Dim dbconn As String = ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString
Using myConnection As New SqlConnection(dbconn)
myConnection.Open()
Dim cmdTerms As New SqlCommand(selectTerms, myConnection)
cmdTerms.Parameters.AddWithValue("#Ref", myUser.ToString())
Dim readerTerms As SqlDataReader = cmdTerms.ExecuteReader()
readerTerms.Read()
termsCompleted = readerTerms.Item(0)
readerTerms.Close()
myConnection.Close()
End Using
End Get
End Property
I am them using the following on each page that is relevant to deny access and redirect (in the page_load):
If Variables.termsCompleted = False Then
Response.Redirect("Terms.aspx")
End If
While this works ok, i'm interested in how secure it is, and is there a better way to do this?
Thanks
Have you considered retrieving the information once during Session_Start, and carrying it around in Session so that you can interrogate it any time you want?
If you can't retrieve the data during authentication/authorization, you would retrieve the data in the same way as you show above.
To put the value into Session: Session["termsCompleted"] = "true";
To read the value from Session: if (Session["termsCompleted"] == "true")....
As an alternative, you could add the information to HttpContext.Current.User.
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' Code that runs when a new session is started
Dim selectTerms As String = "SELECT Terms FROM tblPersonal WHERE Ref=#Ref"
If Request.IsAuthenticated = True Then
Dim dbconn As String = ConfigurationManager.ConnectionStrings("ApplicationServices").ConnectionString
Using myConnection As New SqlConnection(dbconn)
myConnection.Open()
Dim cmdTerms As New SqlCommand(selectTerms, myConnection)
cmdTerms.Parameters.AddWithValue("#Ref", Variables.myUser)
Dim readerTerms As SqlDataReader = cmdTerms.ExecuteReader()
readerTerms.Read()
Session("termsCompleted") = readerTerms.Item(0)
readerTerms.Close()
myConnection.Close()
End Using
End If
End Sub
And in the code-behind:
If Session("termsCompleted") = False Then
Response.Redirect("Terms.aspx")
End If
Unfortunately this is redirecting to the terms.aspx page every time regardless of what is in the database. From debugging it's picking up the reader item as 'False' even when it's true..
Thanks
Create a base page and have each page inherit from that. In this base page you can do the data access once to perform this check. Then store it in session state.
I don't think you have a security issue...I think it's more of a best practice issue. It's not good practice to put your data access requests in a property. In projects I work on, I typically will have a class that has functions that handle my data access with a buisiness layer that makes the calls to my data access. An n-tier project design may not fit your project...I'm just speaking from my experience.
If you need to reuse the bit flag, just store it in Session.
This logic doesn't really belong on a page. If accepting the terms of use is a requirement for accessing parts of your site then you should handle it that way. This problem is a very similar situation to having an administrator section of a site that only a few users can access.
Ideally this is something you would handle before the request gets to the page. There are (at least) two ways to approach this.
You could write a custom HTTP module that subscribes to the AuthorizeRequest event. It will check whether this page requires that you accept terms of agreement or not and if so checks to see if the user has or not. If not it will redirect the user to the terms of use page.
The other option is to put this code into your Global.ascx file. You would want to subscribe to the AuthorizeRequest event and perform your logic there.
I don't think it matters which option you chose (though the second one may be a little more straight forward to implement). The benefit is that this concern is handled outside of the page itself. This way as you add new pages to your site, you can't forget to add your validation code to it. If you ever decide that in addition to accepting terms of agreement users need to do something else, you now have one place to change instead of going through all of the pages, etc.
You should also take advice of some of the other answers and store this value into the Session to avoid having to to do a database request every time a page loads.
More information:
Http Modules and handlers
How To Create an ASP.NET HTTP Module Using Visual C# .NET (VB should be the same concept with VB syntax).
Application, Page and Control lifecycle (to help you better understand how ASP.NET application lifecycle works and what else is possible).

Can't get Session variable the way I want to

Partial Class Preferences_MyPreferences
Inherits System.Web.UI.Page
Dim userID As String = Session("UserID")
This is just a page in asp.net. I want to be able to grab the Session("UserID") but every time I try, I get this error:
Session state can only be used when enableSessionState is set to true, either in a configuration file or in the Page directive. Please also make sure that System.Web.SessionStateModule or a custom session state module is included in the \\ section in the application configuration.
If I put that Dim userID inside say the Page_Load Event, then it works fine. Why does it have to be inside an event? I want to dim it once, and use it throughout the page.
Consider wrapping your call to the Session in a property in your code behind?
Public ReadOnly Property UserID() As String
Get
Return Session("UserID")
End Get
End Property
If you declare it as you have there, the variable is initialized and the session variable is expected to be ready for usage, but it is too early in the page life cycle to allow that. The preferred method would be as #p.campbell has suggested and wrap it in a Property or similar method. To answer the question though, the exception is generated because you are attempting to use session before it is available.
You need to read up on ASP.NET Page Lifecycles. The Session object doesn't become available until a certain point in the page lifecycle; if you want to grab the UserID once, you need to do it after the session becomes available.
The reason it doesn't work in your example is that the constructor for your Preferences_MyPreferences page is executed before the request object is available to the page. You should instead load it during the Page_Init or Page_Load event.

Resources