WEB460 Lab 5 of 7: Refactoring and Security

Availability: In stock

Regular Price: $15.00

Special Price: $12.00

OR

WEB460 Lab 5 of 7 Refactoring and Security Login Page

Double click on above image to view full picture

Zoom Out
Zoom In

More Views

Quick Overview

WEB460 Lab 5 of 7: Refactoring and Security


Scenario/Summary
In this week's Lab, we refactor our web application and add a secure login feature.


Part I: Refactoring
As applications grow and change with added features and fixed bugs, they can become unwieldy and very difficult to maintain. When an application is difficult to maintain, even a small change to the app can result in hard-to-find bugs. The original design of the application may no longer support the features and functionality present in the current application version.
The solution to this situation is refactoring the application code to better distribute the workload among the classes in the application. Refactoring does not change the external functionality of software; it changes the internal structure by reorganizing it. After refactoring our application, it should look the same to users but execute more efficiently and be easier to maintain.
pgCheckOut.aspx.cs has become fairly large, making it difficult to maintain. The reason for this is that we have the pgCheckOut class doing much of the work that belongs in the business layer and data layer. For this week, the goals of our refactoring are as follows.
1. Correctly distributing application functionality
The presentation layer (pgCheckOut) manages user interaction through the ASP form. It makes requests of the business layer for data to populate form fields, responds to button
presses, and sends form data to the business layer for processing.
The business layer (clsBusinessLayer) validates data, catches exceptions, and corrects them if possible. This layer handles communication between our application and others (through an XML file) and retrieves data from the data layer.
The data layer (clsDataLayer) interacts with database and responds to requests formthe business layer. When we are finished with our refactoring, pgCheckOut.aspx.cs should not need any knowledge or interaction with the data layer (clsDataLayer.cs)


2.Replace repeated or duplicate code with methods to improve maintainability We want to replace code that is duplicated in several places with either a method when it is a sequence of statements, or a data field when it is a reused piece of data, such as the data directory's Server.MapPath. We already reduce duplication on our forms by using master pages. This step allows us to reduce duplication in our code.


Part II: Secure Login
Because our application manages customer information, we should provide a secure login so that only authorized users can view and manipulate customer information. Our secure login page accepts a username and password. If the same user fails to correctly enter his or her password three times, that user's account is locked and the person must contact the system admin to unlock it. If there is a total of six failed login attempts during the session, no matter the user, the application locks by hiding the login/submit button.


Here is an outline of this lab:
PART 0: Prepare Your Project
Start your project as a copy of the Week 4 Lab


PART I: STEP A: Move the GetAllCustomers Functionality to the Business Layer
Add a method to clsBusinessLayer.cs and modify a method in pgCheckOut.aspx.cs.


PART I: STEP B: Move Update and Insert Customer Functionality to the Business Layer
Add two methods to clsBusinessLayer.cs, add a new method to pgCheckOut.aspx.cs, and modify two other methods in pgCheckOut.aspx.cs.


PART I: STEP C: Move Retrieving a Single Customer's Data to the Business Layer
Add a method to clsBusinessLayer.cs and modify a method in pgCheckOut.aspx.cs.


PART II: STEP D: Data Layer Functionality: Validating and Locking Users
Add two methods to clsDataLayer.cs.


Lab Steps
PART II: STEP E: Implement the Business Layer Functionality to Verify User Credentials
Add one method to clsBusinessLayer.cs.


PART II: STEP F: Create the Login Form
Create a form, user the site master page and add controls.


PART II: STEP G: Implement Login Functionality
Validate user credentials and redirect to pgCheckOut.aspx.


PART II: STEP H: Harden Your Application (Optional)
Optionally add features to obscure passwords and prevent SQL injection.


PART II: STEP I: Finalize the Lab
Deliverables
Please zip and submit the entire web project folder.


PART 0: Prepare Your Project
To begin this week's lab, create an Empty Web Site and copy the files from the Week 4 Lab into your website folder.
Test your application. It should function the same as it did at the end of the Week 4 Lab.


PART I: STEP A: Move the GetAllCustomersFunctionality to the Business Layer
We begin with an easy change. In pgCheckOut, the method BindCustomerGridView calls the clsDataLayer method GetAllCustomers. Calling this method should be done in the businesslayer (clsBusinessLayer) because the presentation layer (pgCheckOut) should not interact with the data layer. To fix this, we need to take two actions:
Create a method SelectAllCustomers in clsBusinessLayer that calls the clsDataLayer method GetAllCustomers.
Modify BindCustomerGridView to call SelectAllCustomers in clsBusinessLayer.


1. In clsBusinessLayer, add the SelectAllCustomers method:
SelectAllCustomer Method in clsBusinessLayer.cs
public dsAccounts SelectAllCustomers(){
return myDataLayer.GetAllCustomers();
}


2. In the BindCustomerGridView method in pgCheckOut.aspx.cs, change the line where GetAllCustomers is called to be:
dsAccounts customerListing = myBusinessLayer.SelectAllCustomers();


3. Test your application. It should function as it did at the end of the Week 4 Lab.


PART I: STEP B: Move Update and Insert CustomerFunctionality to the Business Layer
The btnAdd_Click and btnUpdate_Click methods in pgCheckOut have very similar functionality. They pass data from the form to the data layer and then handle any exceptions and errors that may occur. After that, both methods clear the form, display a message to the user, and bind the GridView. There are several optimizations we can make with these:
- Move the calls to the data layer and all exception handling into the business layer(clsBusinessLayer) where they belong. The business layer should also handle any exceptions or errors that could arise, relieving the presentation layer of this responsibility.
- Have the business layer methods return a string that indicates success or failure and can be displayed to the user.
- Place the duplicate code that clears the form, binds the GridView, and sets the feedback message in a private method that can be called from the btnAdd_Click and btnUpdate_Click methods. This should make maintaining the application easier.


1.Create the method UpdateCustomer in clsBusinessLayer.cs:
UpdateCustomer Method in clsBusinessLayer.cs
public string UpdateCustomer ( string firstName, string lastName, string street, string city, string state, string phoneNumber, int customerID)
{
// Add your comments here
string resultMessage = "Customer Updated Successfully.";
// Add your comments here
try
{
myDataLayer.UpdateCustomer(firstName,lastName,street,city,state,phoneNumber,customerID);
}
catch(Exception error)
{
resultMessage = "Error updating customer, please check form data. ";
resultMessage = resultMessage+error.Message;
}
return resultMessage;
}


2. Create the method InsertCustomer in clsBusinessLayer.cs:
InsertCustomer Method in clsBusinessLayer.cs
public string InsertCustomer ( string firstName, string lastName, string street, string city, string state, string phoneNumber)
{
// Add your comments here
string resultMessage = "Customer Added Successfully.";
// Add your comments here
try
{
// Add your comments here
myDataLayer.InsertCustomer(firstName,lastName,street,city,state,phoneNumber);
}
catch(Exception error)
{
// Add your comments here
resultMessage = "Error adding customer, please check form data.";
resultMessage = resultMessage + error.Message;
}
return resultMessage;
}


3. Create the private method updateForm in pgCheckOut.aspx.cs that clears the form, displays the message sent as a parameter, and binds the GridView:
updateForm Method in pgCheckOut.aspx.cs
private void updateForm(string results)
{
// Add your comments here
ClearInputs(Page.Controls);
// Add your comments here
Master.UserFeedBack.Text = results;
// Add your comments here
BindCustomerGridView();
}


4. Modify btnUpdate_Click in pgCheckOut.aspx.cs so that it calls UpdateCustomer in clsBusinessLayer and updateForm. The final version of the method should appear as below:
Revised btnUpdate_Click Method in pgCheckOut.aspx.cs
protected void btnUpdate_Click( object sender, EventArgs e)
{
// Add your comments here
string results = myBusinessLayer.UpdateCustomer(txtFirstName.Text,txtLastName.Text,txtStreet.Text,txtCity.Text,txtState.Text,txtPhone.Text,Convert.ToInt32(customerID.Text));
// Add your comments here
updateForm(results);
}


5. Modify btnAdd_Click in pgCheckOut.aspx.cs so that it calls InsertCustomer in clsBusinessLayer and updateForm. The final version of the method should appear as below:
Revised btnAdd_Click Method in pgCheckOut.aspx.cs
protected void btnAdd_Click (object sender,EventArgs e)
{
// Add your comments here
string results = myBusinessLayer.InsertCustomer(txtFirstName.Text,txtLastName.Text,txtStreet.Text,txtCity.Text,txtState.Text,txtPhone.Text);
// Add your comments here
updateForm(results);
}


6. Test your application. It should function the same is it did at the end of the Week 4 Lab.


PART I: STEP C: Move Retrieving a Single Customer's Data to the Business Layer
In this step, we move the functionality that accesses the data layer and performs exception handling from pgCheckOut to clsBusinessLayer as in the previous steps. Generally, fetching records from the database is safer than updating and inserting. If there is a problem, a DataSet object is still created but it contains no records. Our presentation layer can test for that and display a message to the user that "No records were found", just as our current method does.
If a record is returned from the database, empty fields in the record may contain the value DBNull, which represents a NULL value in the database. Because of this, an exception may occur when a field's value is retrieved so it can be placed in a TextBox on the form. We want to prevent any exceptions from occurring in the presentation layer, so our business layer must find a way to fix fields that have a value of DBNull before the DataSet is passed to the presentation layer for display on the form.


1. Here is the code for the FindCustomer method in clsBusinessLayer. Notice that before returning the DataSet to the presentation layer, it checks each field for DBNull. If a field has that value, it is set to the empty string, which is safe for a form to display.
FindCustomer Method in clsBusinessLayer.cs
public dsAccounts FindCustomer (string LastName)
{
// Add your comments here
dsAccounts dsFoundCustomer = myDataLayer.FindCustomer(LastName);
// Add your comments here
if(dsFoundCustomer.tblCustomers.Rows.Count>0)
{
// Add your comments here
System.Data.DataRow customerRecord = dsFoundCustomer.tblCustomers.Rows[0];
if( customerRecord["FirstName"]==DBNull.Value)
customerRecord["FirstName"]=string.Empty;
if(customerRecord["LastName"]==DBNull.Value)
customerRecord["LastName"]=string.Empty;
if(customerRecord["Street"]==DBNull.Value)
customerRecord["Street"]=string.Empty;
if(customerRecord["City"]==DBNull.Value)
customerRecord["City"]=string.Empty;
if(customerRecord["State"]==DBNull.Value)
customerRecord["State"]=string.Empty;
if(customerRecord["PhoneNumber"]==DBNull.Value)
customerRecord["PhoneNumber"]=string.Empty;
}
return dsFoundCustomer;
}


2. Because the business layer handles all the problems, our presentation layer can simply take the work of the business layer and display it for the user. Here is the final version of the modified btnFindLastName_Click method in pgCheckOut.aspx.cs:
Revised btnFindLastName_Click Method in pgCheckOut.aspx.cs
protected void btnFindLastName_Click (object sender, EventArgs e)
{
// Add your comments here
dsAccounts dsFindLastName = myBusinessLayer.FindCustomer(txtLastName.Text);
// Add your comments here
if(dsFindLastName.tblCustomers.Rows.Count>0)
{
// Add your comments here
txtFirstName.Text=dsFindLastName.tblCustomers[0].FirstName;
txtLastName.Text=dsFindLastName.tblCustomers[0].LastName;
txtStreet.Text=dsFindLastName.tblCustomers[0].Street;
txtCity.Text=dsFindLastName.tblCustomers[0].City;
txtState.Text=dsFindLastName.tblCustomers[0].State;
txtPhone.Text=dsFindLastName.tblCustomers[0].PhoneNumber;
customerID.Text=dsFindLastName.tblCustomers[0].CustomerID.ToString();
Master.UserFeedBack.Text="Record Found";
}
else
{
// Add your comments here
Master.UserFeedBack.Text="No records were found!";
}
}


3. Test your application. It should function the same is it did at the end of the Week 4 Lab. You should also scan through your code to see if there are any other optimizers or clean-ups you can make, such as removing statement setting tempPath because we no longer need it.
Refactored Code
At this point, even though your application has no new features compared to the Week 4 Lab, it is much easier to maintain and modify.
As you scroll through pgCheckOut.aspx.cs, you'll see the class has been greatly simplified and the code performs two main tasks in keeping with the rules of the presentation layer:
Passing data from the form to the business layer for processing
Receiving data from the business layer to display on the form
The data layer code in clsDataLayer.cs is only for interacting with the database. It doesn't validate or display data.
The business layer, clsBusinessLayer.cs ,is the workhorse layer. It communicates data requests from the presentation layer to the data layer, handles errors, and passes clean data back to the presentation layer. It also handles communication with outside applications through XML files. In the next part of the lab, we'll see how we can apply business rules
in the business layer also.


PART II: STEP D: Data Layer Functionality: Validating and Locking Users
As we implement our secure login, we'll begin at the lowest tier and work our way up. The Microsoft Access database Accounts.mdb has two tables. tblCustomers, which we have been working with so far, contains the site customers. The second table, tblUsers, has user account information, such as login credentials. Our site users are company employees and are not customers.


1. To validate a user's login credentials, we need to match the username and password to a username and password in the database. We can do that with a simple select that returns a record if the username and password match someone in the database. We then return a bool to indicate whether the credentials were valid.
ValidateUser Method in clsDataLayer.cls
public bool ValidateUser (string username, string passwd)
{
// Add your comments here
dbConnection.Open();
// Add your comments here
string sqlStmt = "SELECT * FROM tblUsers WHERE UserID = @id AND Pwd = @passwd AND Locked = FALSE";
// Add your comments here
OleDbCommand dbCommand = new OleDbCommand(sqlStmt,dbConnection);
// Add your comments here
dbCommand.Parameters.Add(new OleDbParameter("@id",username));
dbCommand.Parameters.Add(new OleDbParameter("@passwd",passwd));
// Add your comments here
OleDbDataReader dr = dbCommand.ExecuteReader();
//Add your comments here
Boolean isValidAccount = dr.Read();
//Add your comments here
dbConnection.Close();
returnis ValidAccount;
}


2. If the business layer has decided a user needs to be locked out of the application, the data layer can oblige this request by setting the LOCKED field for the user to TRUE. Because the SELECT in the previous method would only return a record if the LOCKED field was FALSE, setting LOCKED to TRUE prevents the user's record from ever being found, effectively locking them out.
LockUserAccount Method in clsDataLayer
public void LockUserAccount(string username)
{
// Add your comments here
dbConnection.Open();
// Add your comments here
string sqlStmt = "UPDATE tblUsers SET Locked = True WHERE UserID = @id";
// Add your comments here
OleDbCommand dbCommand=new OleDbCommand(sqlStmt,dbConnection);
// Add your comments here
dbCommand.Parameters.Add(new OleDbParameter("@id",username));
//Add your comments here
dbCommand.ExecuteNonQuery();
//Add your comments here
dbConnection.Close();
}


PART II: STEP E: Implement the Business Layer Functionality to Verify User Credentials
The business layer calls the data layer to validate login credentials, but must also implement our business rules restricting the number of login attempts. We have two rules that must be implemented:
If the same user fails to correctly enter his or her password three times, that user's account is locked and the person must contact the system admin to unlock it.
If there is a total of six failed login in attempts during the session, no matter the user, the application locks by hiding the login/submit button.
To accomplish these tasks, clsBusinessLayer needs access to the Session variable. This is only directly available on forms, so our presentation layer form will need to pass that to the business layer as an argument.
Here is the method we need to add to our business layer to validate the user's credentials and possibly lock the user's account:
CheckUserCredentials Method in clsBusinessLayer
public bool CheckUserCredentials(System.Web.SessionState.HttpSessionState currentSession, string username, string passwd)
{
// Add your comments here
currentSession["LockedSession"] = false;
// Add your comments here
int totalAttempts = Convert.ToInt32(currentSession["AttemptCount"])+1;
currentSession["AttemptCount"]=totalAttempts;
// Add your comments here
int userAttempts=Convert.ToInt32(currentSession[username])+1;
currentSession[username]=userAttempts;
// Add your comments here
if((userAttempts>3)||(totalAttempts>6))
{
currentSession["LockedSession"]=true;
myDataLayer.LockUserAccount(username);
}
return myDataLayer.ValidateUser(username,passwd);
}


PART II: STEP F: Create the Login Form
Now that the backend processing code is complete, we need to create a login form.


1. Create a new Web Form named pgLogin.aspx.
2. Edit the form to use the master page we created in Week 2, Web460Store.master.
3. In the left content area of the form, add the following fields:
Control Type ID / Name Value
Label lblUserID User ID:
TextBox txtUserID
Label lblPassword Password:
TextBox txtPassword **Should have TextMode set to Password
Button btnLogin Login
4. When you are finished, the form in design view should look similar to this image:


PART II: STEP G: Implement Login functionality
1. Last, we'll implement the code for the pgLogin form to tie together all the previous work in this lab. In the Click handler for btnLogin, we'll pass the login credentials to the business layer for verification. If the user is a valid user, we'll redirect to pgCheckOut.aspx.cs. If the account is locked, we display the appropriate message and hide the Login
button to prevent further login attempts. If the user simply entered an incorrect password or username, we'll display a message indicating the mistake.
btnLogin_Click Method in pgLogin.aspx.cs
protected void btnLogin_Click(object sender,EventArgs e)
{
// Add your comments here
// Please verify that your data directory path is correct!!!!
clsBusinessLayer myBusinessLayer = new clsBusinessLayer(Server.MapPath("~/App_Data/"));
// Add your comments here
bool isValidUser = myBusinessLayer.CheckUserCredentials(Session,txtUserID.Text,txtPassword.Text);
// Add your comments here
if(isValidUser)
{
// Add your comments here
Response.Redirect("~/pgCheckOut.aspx");
}
else if(Convert.ToBoolean(Session["LockedSession"]))
{
Master.UserFeedBack.Text="Account is disabled. Contact System Administrator";
// Hide login button :-)
btnLogin.Visible=false;
}
else
{
// Add your comments here
Master.UserFeedBack.Text="The User ID and/or Password supplied is incorrect. Please try again!";
}
}


2. We can modify the Page_Load method in pgLogin to prompt the user for their credentials.
Revised Page_Load Method in pgLogin.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
Master.UserFeedBack.Text="Please enter username and password.";
}


3. Set pgLogin.aspx as your Start Page and test your application. You should be able to log in with the username jsmith and password password1 or jdoe and password2. Also test the validation and lockout code.


PART II: STEP H: Harden Your Application (Optional)
This is an optional step that you can complete on your own. Below are two actions that can make our web application even more secure.
SQL Injection Prevention
Our application is vulnerable to an SQL injection attack since ValidateUser only checks to see if a record is found and not if the found record is the correct user record. Attackers can fool our application into always returning records by sending carefully crafted SQL as the username or password on our login form. The ValidateUser method will then return true
as if the correct username and password were given.
A quick fix for this is to modify our Data Layer ValidateUser method to verify that the username in the record returned matches the one we were sent as an argument. To implement this, you need to change the query to be similar to the query used in the FindCustomer method. Then compare the UserID field in the record returned from the database to the username
sent as an argument.
Encrypting Passwords in the Database
Our application stores user passwords in plaintext. If an attacker gains access to our server through a vulnerability in our application or possibly another application on the same server,user account passwords are exposed and easily compromised. The passwords should be stored as SHA1 hashes which will make them nearly impossible to reverse engineer.
Unless you have a hashing application available, it is easiest to create new user accounts and store the passwords for these new users as the SHA1 hash. Once you have new users created with hashed passwords stored in the database, you can delete the current user accounts that have unencrypted passwords.
Here is an overview of the steps needed to implement securely stored passwords:
- On the form pgLogin.aspx, add fields (User ID,Password,Verify Password) and a button to Create New User Account.
- Verify the two password fields match.
- Create a new record in tblUsers for this user and store the SHA1 hash of the password instead of the password.
- When a user logs in, search for the username in the database.
-- If a record is found, compute the SHA1 hash of the password the user supplied during the login attempt. Compare the SHA1 of the password entered by the user to the SHA1 password hash stored in the database. If they match, we have a valid user.
The following code uses the ASP .NET class SHA1CryptoServiceProvider to create theSHA1 hash of the variable userPassword and store it in the variable hashedPassword:
UnicodeEncoding encoding = new UnicodeEncoding();
SHA1 sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedPassword = new byte[encoding.GetByteCount(userPassword)];
hashedPassword=sha1Hasher.ComputeHash(hashedPassword);
You can make your application even more secure by using salt with a SHA1 password hash, but that is beyond the scope what we can cover here.


1. Optionally add features to obscure passwords and prevent SQL injection
2. Finalize the Lab


PART II: STEP I: Finalize the Lab
1. Save your work!
2. Test it! Make changes as appropriate until it works. Try adding new records to the database.
3. Remember to add comments for each step being performed.
4. Please zip and submit the entire web project folder.

Regular Price: $15.00

Special Price: $12.00

Details

WEB460 Lab 5 of 7: Refactoring and Security

Scenario/Summary
In this week's Lab, we refactor our web application and add a secure login feature.

Part I: Refactoring
As applications grow and change with added features and fixed bugs, they can become unwieldy and very difficult to maintain. When an application is difficult to maintain, even a small change to the app can result in hard-to-find bugs. The original design of the application may no longer support the features and functionality present in the current application version.
The solution to this situation is refactoring the application code to better distribute the workload among the classes in the application. Refactoring does not change the external functionality of software; it changes the internal structure by reorganizing it. After refactoring our application, it should look the same to users but execute more efficiently and be easier to maintain.
pgCheckOut.aspx.cs has become fairly large, making it difficult to maintain. The reason for this is that we have the pgCheckOut class doing much of the work that belongs in the business layer and data layer. For this week, the goals of our refactoring are as follows.
1. Correctly distributing application functionality
The presentation layer (pgCheckOut) manages user interaction through the ASP form. It makes requests of the business layer for data to populate form fields, responds to button
presses, and sends form data to the business layer for processing.
The business layer (clsBusinessLayer) validates data, catches exceptions, and corrects them if possible. This layer handles communication between our application and others (through an XML file) and retrieves data from the data layer.
The data layer (clsDataLayer) interacts with database and responds to requests formthe business layer. When we are finished with our refactoring, pgCheckOut.aspx.cs should not need any knowledge or interaction with the data layer (clsDataLayer.cs)

2.Replace repeated or duplicate code with methods to improve maintainability We want to replace code that is duplicated in several places with either a method when it is a sequence of statements, or a data field when it is a reused piece of data, such as the data directory's Server.MapPath. We already reduce duplication on our forms by using master pages. This step allows us to reduce duplication in our code.

Part II: Secure Login
Because our application manages customer information, we should provide a secure login so that only authorized users can view and manipulate customer information. Our secure login page accepts a username and password. If the same user fails to correctly enter his or her password three times, that user's account is locked and the person must contact the system admin to unlock it. If there is a total of six failed login attempts during the session, no matter the user, the application locks by hiding the login/submit button.

Here is an outline of this lab:
PART 0: Prepare Your Project
Start your project as a copy of the Week 4 Lab

PART I: STEP A: Move the GetAllCustomers Functionality to the Business Layer
Add a method to clsBusinessLayer.cs and modify a method in pgCheckOut.aspx.cs.

PART I: STEP B: Move Update and Insert Customer Functionality to the Business Layer
Add two methods to clsBusinessLayer.cs, add a new method to pgCheckOut.aspx.cs, and modify two other methods in pgCheckOut.aspx.cs.

PART I: STEP C: Move Retrieving a Single Customer's Data to the Business Layer
Add a method to clsBusinessLayer.cs and modify a method in pgCheckOut.aspx.cs.

PART II: STEP D: Data Layer Functionality: Validating and Locking Users
Add two methods to clsDataLayer.cs.

Lab Steps
PART II: STEP E: Implement the Business Layer Functionality to Verify User Credentials
Add one method to clsBusinessLayer.cs.

PART II: STEP F: Create the Login Form
Create a form, user the site master page and add controls.

PART II: STEP G: Implement Login Functionality
Validate user credentials and redirect to pgCheckOut.aspx.

PART II: STEP H: Harden Your Application (Optional)
Optionally add features to obscure passwords and prevent SQL injection.

PART II: STEP I: Finalize the Lab
Deliverables
Please zip and submit the entire web project folder.

PART 0: Prepare Your Project
To begin this week's lab, create an Empty Web Site and copy the files from the Week 4 Lab into your website folder.
Test your application. It should function the same as it did at the end of the Week 4 Lab.

PART I: STEP A: Move the GetAllCustomersFunctionality to the Business Layer
We begin with an easy change. In pgCheckOut, the method BindCustomerGridView calls the clsDataLayer method GetAllCustomers. Calling this method should be done in the businesslayer (clsBusinessLayer) because the presentation layer (pgCheckOut) should not interact with the data layer. To fix this, we need to take two actions:
Create a method SelectAllCustomers in clsBusinessLayer that calls the clsDataLayer method GetAllCustomers.
Modify BindCustomerGridView to call SelectAllCustomers in clsBusinessLayer.

1. In clsBusinessLayer, add the SelectAllCustomers method:
SelectAllCustomer Method in clsBusinessLayer.cs
public dsAccounts SelectAllCustomers(){
return myDataLayer.GetAllCustomers();
}

2. In the BindCustomerGridView method in pgCheckOut.aspx.cs, change the line where GetAllCustomers is called to be:
dsAccounts customerListing = myBusinessLayer.SelectAllCustomers();

3. Test your application. It should function as it did at the end of the Week 4 Lab.

PART I: STEP B: Move Update and Insert CustomerFunctionality to the Business Layer
The btnAdd_Click and btnUpdate_Click methods in pgCheckOut have very similar functionality. They pass data from the form to the data layer and then handle any exceptions and errors that may occur. After that, both methods clear the form, display a message to the user, and bind the GridView. There are several optimizations we can make with these:
- Move the calls to the data layer and all exception handling into the business layer(clsBusinessLayer) where they belong. The business layer should also handle any exceptions or errors that could arise, relieving the presentation layer of this responsibility.
- Have the business layer methods return a string that indicates success or failure and can be displayed to the user.
- Place the duplicate code that clears the form, binds the GridView, and sets the feedback message in a private method that can be called from the btnAdd_Click and btnUpdate_Click methods. This should make maintaining the application easier.

1.Create the method UpdateCustomer in clsBusinessLayer.cs:
UpdateCustomer Method in clsBusinessLayer.cs
public string UpdateCustomer ( string firstName, string lastName, string street, string city, string state, string phoneNumber, int customerID)
{
// Add your comments here
string resultMessage = "Customer Updated Successfully.";
// Add your comments here
try
{
myDataLayer.UpdateCustomer(firstName,lastName,street,city,state,phoneNumber,customerID);
}
catch(Exception error)
{
resultMessage = "Error updating customer, please check form data. ";
resultMessage = resultMessage+error.Message;
}
return resultMessage;
}

2. Create the method InsertCustomer in clsBusinessLayer.cs:
InsertCustomer Method in clsBusinessLayer.cs
public string InsertCustomer ( string firstName, string lastName, string street, string city, string state, string phoneNumber)
{
// Add your comments here
string resultMessage = "Customer Added Successfully.";
// Add your comments here
try
{
// Add your comments here
myDataLayer.InsertCustomer(firstName,lastName,street,city,state,phoneNumber);
}
catch(Exception error)
{
// Add your comments here
resultMessage = "Error adding customer, please check form data.";
resultMessage = resultMessage + error.Message;
}
return resultMessage;
}

3. Create the private method updateForm in pgCheckOut.aspx.cs that clears the form, displays the message sent as a parameter, and binds the GridView:
updateForm Method in pgCheckOut.aspx.cs
private void updateForm(string results)
{
// Add your comments here
ClearInputs(Page.Controls);
// Add your comments here
Master.UserFeedBack.Text = results;
// Add your comments here
BindCustomerGridView();
}

4. Modify btnUpdate_Click in pgCheckOut.aspx.cs so that it calls UpdateCustomer in clsBusinessLayer and updateForm. The final version of the method should appear as below:
Revised btnUpdate_Click Method in pgCheckOut.aspx.cs
protected void btnUpdate_Click( object sender, EventArgs e)
{
// Add your comments here
string results = myBusinessLayer.UpdateCustomer(txtFirstName.Text,txtLastName.Text,txtStreet.Text,txtCity.Text,txtState.Text,txtPhone.Text,Convert.ToInt32(customerID.Text));
// Add your comments here
updateForm(results);
}

5. Modify btnAdd_Click in pgCheckOut.aspx.cs so that it calls InsertCustomer in clsBusinessLayer and updateForm. The final version of the method should appear as below:
Revised btnAdd_Click Method in pgCheckOut.aspx.cs
protected void btnAdd_Click (object sender,EventArgs e)
{
// Add your comments here
string results = myBusinessLayer.InsertCustomer(txtFirstName.Text,txtLastName.Text,txtStreet.Text,txtCity.Text,txtState.Text,txtPhone.Text);
// Add your comments here
updateForm(results);
}

6. Test your application. It should function the same is it did at the end of the Week 4 Lab.

PART I: STEP C: Move Retrieving a Single Customer's Data to the Business Layer
In this step, we move the functionality that accesses the data layer and performs exception handling from pgCheckOut to clsBusinessLayer as in the previous steps. Generally, fetching records from the database is safer than updating and inserting. If there is a problem, a DataSet object is still created but it contains no records. Our presentation layer can test for that and display a message to the user that "No records were found", just as our current method does.
If a record is returned from the database, empty fields in the record may contain the value DBNull, which represents a NULL value in the database. Because of this, an exception may occur when a field's value is retrieved so it can be placed in a TextBox on the form. We want to prevent any exceptions from occurring in the presentation layer, so our business layer must find a way to fix fields that have a value of DBNull before the DataSet is passed to the presentation layer for display on the form.

1. Here is the code for the FindCustomer method in clsBusinessLayer. Notice that before returning the DataSet to the presentation layer, it checks each field for DBNull. If a field has that value, it is set to the empty string, which is safe for a form to display.
FindCustomer Method in clsBusinessLayer.cs
public dsAccounts FindCustomer (string LastName)
{
// Add your comments here
dsAccounts dsFoundCustomer = myDataLayer.FindCustomer(LastName);
// Add your comments here
if(dsFoundCustomer.tblCustomers.Rows.Count>0)
{
// Add your comments here
System.Data.DataRow customerRecord = dsFoundCustomer.tblCustomers.Rows[0];
if( customerRecord["FirstName"]==DBNull.Value)
customerRecord["FirstName"]=string.Empty;
if(customerRecord["LastName"]==DBNull.Value)
customerRecord["LastName"]=string.Empty;
if(customerRecord["Street"]==DBNull.Value)
customerRecord["Street"]=string.Empty;
if(customerRecord["City"]==DBNull.Value)
customerRecord["City"]=string.Empty;
if(customerRecord["State"]==DBNull.Value)
customerRecord["State"]=string.Empty;
if(customerRecord["PhoneNumber"]==DBNull.Value)
customerRecord["PhoneNumber"]=string.Empty;
}
return dsFoundCustomer;
}

2. Because the business layer handles all the problems, our presentation layer can simply take the work of the business layer and display it for the user. Here is the final version of the modified btnFindLastName_Click method in pgCheckOut.aspx.cs:
Revised btnFindLastName_Click Method in pgCheckOut.aspx.cs
protected void btnFindLastName_Click (object sender, EventArgs e)
{
// Add your comments here
dsAccounts dsFindLastName = myBusinessLayer.FindCustomer(txtLastName.Text);
// Add your comments here
if(dsFindLastName.tblCustomers.Rows.Count>0)
{
// Add your comments here
txtFirstName.Text=dsFindLastName.tblCustomers[0].FirstName;
txtLastName.Text=dsFindLastName.tblCustomers[0].LastName;
txtStreet.Text=dsFindLastName.tblCustomers[0].Street;
txtCity.Text=dsFindLastName.tblCustomers[0].City;
txtState.Text=dsFindLastName.tblCustomers[0].State;
txtPhone.Text=dsFindLastName.tblCustomers[0].PhoneNumber;
customerID.Text=dsFindLastName.tblCustomers[0].CustomerID.ToString();
Master.UserFeedBack.Text="Record Found";
}
else
{
// Add your comments here
Master.UserFeedBack.Text="No records were found!";
}
}

3. Test your application. It should function the same is it did at the end of the Week 4 Lab. You should also scan through your code to see if there are any other optimizers or clean-ups you can make, such as removing statement setting tempPath because we no longer need it.
Refactored Code
At this point, even though your application has no new features compared to the Week 4 Lab, it is much easier to maintain and modify.
As you scroll through pgCheckOut.aspx.cs, you'll see the class has been greatly simplified and the code performs two main tasks in keeping with the rules of the presentation layer:
Passing data from the form to the business layer for processing
Receiving data from the business layer to display on the form
The data layer code in clsDataLayer.cs is only for interacting with the database. It doesn't validate or display data.
The business layer, clsBusinessLayer.cs ,is the workhorse layer. It communicates data requests from the presentation layer to the data layer, handles errors, and passes clean data back to the presentation layer. It also handles communication with outside applications through XML files. In the next part of the lab, we'll see how we can apply business rules
in the business layer also.

PART II: STEP D: Data Layer Functionality: Validating and Locking Users
As we implement our secure login, we'll begin at the lowest tier and work our way up. The Microsoft Access database Accounts.mdb has two tables. tblCustomers, which we have been working with so far, contains the site customers. The second table, tblUsers, has user account information, such as login credentials. Our site users are company employees and are not customers.

1. To validate a user's login credentials, we need to match the username and password to a username and password in the database. We can do that with a simple select that returns a record if the username and password match someone in the database. We then return a bool to indicate whether the credentials were valid.
ValidateUser Method in clsDataLayer.cls
public bool ValidateUser (string username, string passwd)
{
// Add your comments here
dbConnection.Open();
// Add your comments here
string sqlStmt = "SELECT * FROM tblUsers WHERE UserID = @id AND Pwd = @passwd AND Locked = FALSE";
// Add your comments here
OleDbCommand dbCommand = new OleDbCommand(sqlStmt,dbConnection);
// Add your comments here
dbCommand.Parameters.Add(new OleDbParameter("@id",username));
dbCommand.Parameters.Add(new OleDbParameter("@passwd",passwd));
// Add your comments here
OleDbDataReader dr = dbCommand.ExecuteReader();
//Add your comments here
Boolean isValidAccount = dr.Read();
//Add your comments here
dbConnection.Close();
returnis ValidAccount;
}

2. If the business layer has decided a user needs to be locked out of the application, the data layer can oblige this request by setting the LOCKED field for the user to TRUE. Because the SELECT in the previous method would only return a record if the LOCKED field was FALSE, setting LOCKED to TRUE prevents the user's record from ever being found, effectively locking them out.
LockUserAccount Method in clsDataLayer
public void LockUserAccount(string username)
{
// Add your comments here
dbConnection.Open();
// Add your comments here
string sqlStmt = "UPDATE tblUsers SET Locked = True WHERE UserID = @id";
// Add your comments here
OleDbCommand dbCommand=new OleDbCommand(sqlStmt,dbConnection);
// Add your comments here
dbCommand.Parameters.Add(new OleDbParameter("@id",username));
//Add your comments here
dbCommand.ExecuteNonQuery();
//Add your comments here
dbConnection.Close();
}

PART II: STEP E: Implement the Business Layer Functionality to Verify User Credentials
The business layer calls the data layer to validate login credentials, but must also implement our business rules restricting the number of login attempts. We have two rules that must be implemented:
If the same user fails to correctly enter his or her password three times, that user's account is locked and the person must contact the system admin to unlock it.
If there is a total of six failed login in attempts during the session, no matter the user, the application locks by hiding the login/submit button.
To accomplish these tasks, clsBusinessLayer needs access to the Session variable. This is only directly available on forms, so our presentation layer form will need to pass that to the business layer as an argument.
Here is the method we need to add to our business layer to validate the user's credentials and possibly lock the user's account:
CheckUserCredentials Method in clsBusinessLayer
public bool CheckUserCredentials(System.Web.SessionState.HttpSessionState currentSession, string username, string passwd)
{
// Add your comments here
currentSession["LockedSession"] = false;
// Add your comments here
int totalAttempts = Convert.ToInt32(currentSession["AttemptCount"])+1;
currentSession["AttemptCount"]=totalAttempts;
// Add your comments here
int userAttempts=Convert.ToInt32(currentSession[username])+1;
currentSession[username]=userAttempts;
// Add your comments here
if((userAttempts>3)||(totalAttempts>6))
{
currentSession["LockedSession"]=true;
myDataLayer.LockUserAccount(username);
}
return myDataLayer.ValidateUser(username,passwd);
}

PART II: STEP F: Create the Login Form
Now that the backend processing code is complete, we need to create a login form.

1. Create a new Web Form named pgLogin.aspx.
2. Edit the form to use the master page we created in Week 2, Web460Store.master.
3. In the left content area of the form, add the following fields:
Control Type ID / Name Value
Label lblUserID User ID:
TextBox txtUserID
Label lblPassword Password:
TextBox txtPassword **Should have TextMode set to Password
Button btnLogin Login
4. When you are finished, the form in design view should look similar to this image:

PART II: STEP G: Implement Login functionality
1. Last, we'll implement the code for the pgLogin form to tie together all the previous work in this lab. In the Click handler for btnLogin, we'll pass the login credentials to the business layer for verification. If the user is a valid user, we'll redirect to pgCheckOut.aspx.cs. If the account is locked, we display the appropriate message and hide the Login
button to prevent further login attempts. If the user simply entered an incorrect password or username, we'll display a message indicating the mistake.
btnLogin_Click Method in pgLogin.aspx.cs
protected void btnLogin_Click(object sender,EventArgs e)
{
// Add your comments here
// Please verify that your data directory path is correct!!!!
clsBusinessLayer myBusinessLayer = new clsBusinessLayer(Server.MapPath("~/App_Data/"));
// Add your comments here
bool isValidUser = myBusinessLayer.CheckUserCredentials(Session,txtUserID.Text,txtPassword.Text);
// Add your comments here
if(isValidUser)
{
// Add your comments here
Response.Redirect("~/pgCheckOut.aspx");
}
else if(Convert.ToBoolean(Session["LockedSession"]))
{
Master.UserFeedBack.Text="Account is disabled. Contact System Administrator";
// Hide login button :-)
btnLogin.Visible=false;
}
else
{
// Add your comments here
Master.UserFeedBack.Text="The User ID and/or Password supplied is incorrect. Please try again!";
}
}

2. We can modify the Page_Load method in pgLogin to prompt the user for their credentials.
Revised Page_Load Method in pgLogin.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
Master.UserFeedBack.Text="Please enter username and password.";
}

3. Set pgLogin.aspx as your Start Page and test your application. You should be able to log in with the username jsmith and password password1 or jdoe and password2. Also test the validation and lockout code.

PART II: STEP H: Harden Your Application (Optional)
This is an optional step that you can complete on your own. Below are two actions that can make our web application even more secure.
SQL Injection Prevention
Our application is vulnerable to an SQL injection attack since ValidateUser only checks to see if a record is found and not if the found record is the correct user record. Attackers can fool our application into always returning records by sending carefully crafted SQL as the username or password on our login form. The ValidateUser method will then return true
as if the correct username and password were given.
A quick fix for this is to modify our Data Layer ValidateUser method to verify that the username in the record returned matches the one we were sent as an argument. To implement this, you need to change the query to be similar to the query used in the FindCustomer method. Then compare the UserID field in the record returned from the database to the username
sent as an argument.
Encrypting Passwords in the Database
Our application stores user passwords in plaintext. If an attacker gains access to our server through a vulnerability in our application or possibly another application on the same server,user account passwords are exposed and easily compromised. The passwords should be stored as SHA1 hashes which will make them nearly impossible to reverse engineer.
Unless you have a hashing application available, it is easiest to create new user accounts and store the passwords for these new users as the SHA1 hash. Once you have new users created with hashed passwords stored in the database, you can delete the current user accounts that have unencrypted passwords.
Here is an overview of the steps needed to implement securely stored passwords:
- On the form pgLogin.aspx, add fields (User ID,Password,Verify Password) and a button to Create New User Account.
- Verify the two password fields match.
- Create a new record in tblUsers for this user and store the SHA1 hash of the password instead of the password.
- When a user logs in, search for the username in the database.
-- If a record is found, compute the SHA1 hash of the password the user supplied during the login attempt. Compare the SHA1 of the password entered by the user to the SHA1 password hash stored in the database. If they match, we have a valid user.
The following code uses the ASP .NET class SHA1CryptoServiceProvider to create theSHA1 hash of the variable userPassword and store it in the variable hashedPassword:
UnicodeEncoding encoding = new UnicodeEncoding();
SHA1 sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedPassword = new byte[encoding.GetByteCount(userPassword)];
hashedPassword=sha1Hasher.ComputeHash(hashedPassword);
You can make your application even more secure by using salt with a SHA1 password hash, but that is beyond the scope what we can cover here.

1. Optionally add features to obscure passwords and prevent SQL injection
2. Finalize the Lab

PART II: STEP I: Finalize the Lab
1. Save your work!
2. Test it! Make changes as appropriate until it works. Try adding new records to the database.
3. Remember to add comments for each step being performed.
4. Please zip and submit the entire web project folder.

Additional Information

Special Price $12.00

Product Tags

Use spaces to separate tags. Use single quotes (') for phrases.