COM372: Notes, Chapter 26

Building User Authentication and Personalization
pages 541-578

Syllabus | Grading | Reading Notes | Dr. Logan

To this point, we've been looking at parts of a whole. In this and the next chapter, we build something that looks much like a familiar e-commerce site, complete with access to databases, registering, and being remembered in terms of previous interactions with the site. The chapter is laid out as a client-focused development effort, creating pages to a client's specifications.

The Problem (as specified by the client): Were mimicking some of the functions of a site called Backflip (http://backflip.com—Take a look at it.) Here, you can log in and store personal bookmarks, and get recommendations for sites based on preferences expressed. That is, 1) you need to be able to identify individual users and to authenticate them, 2) you need to be able to store bookmarks for each user, allowing them to add or delete bookmarks, and 3) you need to be able to recommend other sites to users, based on what you know about them already.

Components of the Solution:

Site Overview: A flowchart that will accomplish all of the above is sketched in figure 26.1 (p. 543). All of this is built into the files for chapter 26 (ftp from ...common/Welling/Source/26/), listed in table 26.1. The setup for the database is in table 26.1 and also in the file in ...common/ on the server.

Site Implementation

Login.php (listing 26.2, p. 546; figure 26.3, p. 547): This is modular code to

Registering a User: Users need to be able to do four things: 1)Register, 2)log in and out, 3)change passwords, and 4)reset forgotten passwords. Registering is from the form (called by display_registration_form(), from the library output_fns.php set) on page register_form.php (listing 26.5, p. 549), reached from the "not a member" link on the login.php page. The action for this form is register_new.php (listing 26.6, p. 550-2), which performs all of the functions of our assignment 8. That is, it takes the form values and does error checking (e.g., checks if fields are complete, compares password entered with the confirm-password entered, etc.). It also checks the user against the database and enters the user if the name isn't already used. Error checking uses functions filled_out() (listing 26.7, p. 553) and valid_email() (listing 26.8, p. 553). Registering uses function register() (listing 26.9, p. 555). Connecting to the database uses db_connect() (listing 26.10, p. 555).

Logging in: Registered users log in on page login.php, whose action is member.php (listing 26.11, p. 556-7) the main application page of the site. The page checks the POST collection to see whether the user has just logged in. If so, it validates the login attempt against the database (using function login() (listing 26.12, p. 558-9), and sets a session variable that will be used for page authentication for the rest of the site. If the user has not just logged in, the page checks the session variable for authentication (using function check_valid_user() (listing 26.12, p. 559), and if this is a valid user, it presents content (bookmarks, menus, etc.). This was the pattern created in chapter 22.

Logging out uses listing 26.14 (p. 560), which again was developed in chapter 22.

Changing passwords: From the link in the common page footer, changing password calls change_passwd.php (listing 26.15, p. 561-2), which checks user authenticity and form validation and alters the database.

Resetting forgotten passwords: From the link on login.php, "Forgotten your password?", forgot_form.php asks for a user name, which is passed to forgot_passwd.php (listing 26.17, p. 564), which calls reset_password() (listing 26.18, p. 564-5) and notify_password() (listing 26.20, p. 567). Reset_passwd() generates a random password from a dictionary, using get_random_word() (listing 26.19, p. 565-6, part of the user_auth_fns.php library, and suffixing it with a random number between 0 and 999. Details of the word search are on p. 566, along with instructions for setting up a dictionary on the server.

Notifying the user by email: The new password is passed to notify_password() (listing 26.20, p. 567), which looks up the user in the database, finds the current email address, prepares a simple message and address, and send an email using mail(). The passwords created here are relatively simple, and not highly secure; the email includes the advice to change the password next time.

Implementing Bookmard Storage and Retrieval

Adding bookmarks: Users add bookmarks via the simple form (fig. 26.9), which has the action add_bms.php (listing 26.21, p. 569). This validates the form (filled_out()) first. It then checks the bookmark to make sure it starts with http:// (supplying it if needed), and then checks whether it actually exists (using fopen()) before adding it to the database using add_bm() (listing 26.22, p. 570). Lastly, add_bms.php gets and displays a user's bookmarks, using get_user_urls() (listing 26.23, p. 571) and display_user_urls() (simple tabular output, and not listed, but see fig. 26.6, p. 558).

Deleting bookmarks: The display form includes a checkbox for deletion (fig. 26.6), which has an action delete_bms.php (listing 26.24, p. 572). This takes the POST array from the form and deletes each checked entry (note: this is one record, selected (WHERE...) as a pair of fields made up of the url and the user, which may leave the url paired with another user elsewhere in the table) from the database using function delete_bm() (listing 26.25, p. 573).

Implementing Recommendations

Implementing via subqueries: We're trying to create a list of url's from people who are "like-minded" with our user, meaning they share common url's on their personal list. Do this with subqueries
SELECT distinct(b2.username)
FROM bookmark b1, bookmark b2
WHERE b1.username='$valid_user'
and b1.username != b2.username
and b1.bm_URL = b2.bm_URL

Here, we are looking at the same table twice, using the aliases "b1" and "b2" (abbreviations placed immediately after any table name in any SQL string). We're looking up the user's name (from the session variable) and comparing this user with all other users (i.e., NOT this user), and getting all matching URL's. This creates a recordset, which we place inside an outer query
SELECT bm_URL
FROM bookmark
WHERE username in
(...our subquery, above, goes here).
...but wait, you aren't done! Append to this
and bm_URL not in
SELECT bm_URL FROM bookmark WHERE username='$valid_user')

to filter out bookmarks that are already in the user's list, and
group by bm_url HAVING count(bm_URL)>$popularity
to eliminate low-frequency URL's. (If you can't follow the pieces, see the final SQL on p. 575.)

This is implemented in recommend.php (listing 26.26, p. 576), which calls funtion recommend_urls() (listing 26.27, p. 576-7). Sample output is shown in fig. 26.11 (p. 578).