6.5 User Management - Page 10
July 12, 2002
If a site has user data, it probably needs user management: functions for creating new
user accounts and modifying their information.
A site can offer public registration, which allows anyone to create an account and
access the site, or have private registration where the potential user fills in a form or
sends a mail message to an administrator who then creates an account. Sites that use
private registration might still have a web application to create accounts, although the
application and other administrative functions would be protected from public use.
To fill out the login handler started earlier, we'll add a page for filling in user information
and creating an account. This will also extend the Users table into something
more useful and add goodies such as telling the user when the account was last used.
The code samples here are from CreateUser. pm:
sub handler {
# Receive and parse the request.
my $r = shift;
my $q = Apache::Request->new($r);
my $username = $q->param('username') || '';
my $password = $q->param('password');
my $name = $q->param('name') || '';
my $email = $q->param('email') || '';
This is the standard beginning, checking for and initializing parameters. It also builds
the entry form in a variable, using the parameter values:
# The entry form.
my $form = <<ENDHTML;
<FORM METHOD="POST">
<TABLE>
<TR>
<TD>Desired username:</TD>
<TD><INPUT TYPE="text" NAME="username"
VALUE="$username" SIZE=10></TD>
</TR>
<TR>
<TD>Password:</TD>
<TD><INPUT TYPE="password" NAME="password" SIZE=20></TD>
</TR>
<TR>
<TD>Your real name:</TD>
<TD><INPUT TYPE="text" NAME="name"
VALUE="$name" SIZE=30></TD>
</TR>
<TR>
<TD>Your e-mail address:</TD>
<TD><INPUT TYPE="text" NAME="email"
VALUE="$email" SIZE=24></TD>
</TR>
</TABLE>
<INPUT TYPE="submit" VALUE="Log in">
</FORM>
ENDHTML
The code carries forward the previous values (if any) of the username, name, and
email fields, blanking the password (which isn't echoed anyway). You might recall
from the examples in chapter 3 that CGI. pm held onto parameter values automatically
(it calls them " sticky parameters" ). Coding your own HTML means having to
take care of this yourself or it means not having to work around CGI. pm features,
depending on whom you ask.
If your password creation form doesn't echo password inputs, you should probably
require the user to type the password twice and verify that both inputs are the same
before creating the account. Some registration systems create a random password for
the user and email it to a requested address. In that case, ask for the address twice to
help avoid typos.
# Get all the required inputs
unless ($username && $password) {
my $title = $ENV{'HTTPS'} ?
'Secure login form' : 'Insecure login form';
$r->send_http_header('text/html');
print <<ENDHTML;
<!DOCTYPE HTML PUBLIC>
<HTML>
<HEAD><TITLE>$title</TITLE></HEAD>
<BODY>
<H2>Please fill in the following information:</H2>
$form
</BODY>
</HTML>
ENDHTML
return OK;
}
This section prints the form if the required fields, username and password in this
case, haven't been filled in. A more complex application should give the user a clear
indication of what is required.
Note that when we created the Users table we allowed nulls in the name and email
fields. We also don't require them to be input here. In general, a field which is required
on a form shouldn't allow nulls in a database and vice-versa.
The next section verifies that the username is unique:
# Check for a unique username.
my $dbh = DBI->connect('DBI:mysql:Info:localhost',
'web','nouser');
return error($r, "Can't connect to Info") unless $dbh;
my ($try) = $dbh->selectrow_array
('SELECT password FROM Users WHERE username = ?',
undef, $username);
if ($try) {
$r->send_http_header('text/html');
print <<ENDHTML;
<!DOCTYPE HTML PUBLIC>
<HTML>
<HEAD><TITLE>Already exists</TITLE></HEAD>
<BODY>
<H2>User $username already exists.</H2>
$form
</BODY>
</HTML>
ENDHTML
return OK;
}
The retrieval code is the same as that of the previous example, but in this case a successful
SELECT means that the requested username is taken. It doesn't really matter
what we select from the table, so long as we'll know if the record exists. If not, we can
go on to add the record:
# Encrypt the password.
$password = createPassword($password);
# Write the user record.
$dbh->do('INSERT INTO Users VALUES (?,?,?,?)',
undef, $username, $password, $name, $email)
|| return error($r, $dbh->errstr);
This uses the createPassword function shown earlier to encrypt the password
and then store it in the database. DBI's convenient do function prepares and executes
the given statement. Recall that the first argument after the statement is for
attributes, and the rest are placeholder values. There's nothing left to do but confirm
things with the user.
# Tell the user that they're one of us now.
$r->send_http_header('text/html');
print <<ENDHTML;
<!DOCTYPE HTML PUBLIC>
<HTML>
<HEAD><TITLE>Account created</TITLE></HEAD>
<BODY>
<H2>Account created.</H2>
Welcome to our site.
</BODY>
</HTML>
ENDHTML
return OK;
}
Add this handler to your configuration file and restart Apache as usual:
PerlModule Examples::CreateUser
<Location /create>
SetHandler perl-script
PerlHandler Examples::CreateUser
</Location>
Note that the handler is assigned to /create, the URL which was given in the previous
login example.
Although the creation example is meant to tie in to the login example, it could also
be used by sites with HTTP authentication. The Apache:: DBI module includes an
authentication hook that validates users in a database using DBI. The creation form
could be used to create the necessary record, and regular HTTP authentication takes
over from there.
Application designers should bear in mind that users come to a site for content, not
user management. If a user requests protected pages without logging in, give them the
login page, then go directly to the requested content after the user is validated. HTTP
authentication appears to do this automatically (though actually it is the browser's
authentication cache that quietly identifies the user for each page); techniques for
doing it yourself follow in the next section.
6.4.2 Doing your own authentication (Cont.) - Page 9
Web Development with Apache and Perl
6.6 Login Sessions - Page 11
|