Here is the whole login procedure taken as a whole
Here we put what we have learned previously together into a single
package.
At the same time, we re-visit the earlier work and modify that past effort
to more intelligently take into account the strengths that Perl offers.
use CGI;
$query = new CGI;
print $query->header;
print $query->start_html('login_cgi.pl');
print "This program reads the old student file, creates a scrolling list, and asks you to either choose one (i.e. old student) or input a name (i.e. new student).";
#last_name+first_name+password, where '+' is the delimter
$filename = "students_fall_96.dat";
#now we abandon pseudo FORTRAN/BASIC/etc codes, and do it in Perl fashion
$i = 1;
open ( STUD_FILE , $filename) ;
while(<STUD_FILE>){
chop;
if($_ ne ''){
$line = $_;
$safety_line[$i] = $line;
# print "<br>DEBUG",$i,"->",$safety_line[$i];
$i++;
($last_name,$first_name,$password) = split(/+/,$line);
if($last_name ne ''){ $id{$last_name} = $password;}
push(@last_name,$last_name);
# print $id($last_name),"
";
}
}
$i--;
$nrec = $i;
close(STUD_FILE);
print $query->start_form;
#print $query->radio_group('choice_of_login_mode',['from list',
# 'adding new student'],'from list','true');
print $query->scrolling_list('student_list', @last_name);
#if($query->param('sub1')){
$msg = "Student Registration";
print $query->submit('sub1',$msg);
if ($query->param('sub1')) {
$chosen_name = $query->param('student_list');
print "<br>Enter your password here:",$query->password_field('pswd','',5,8), " and resubmit by pressing `Student Registration' button.";
$entered_passwd = $query->param('pswd');
# print "<br>(DEBUG)$chosen_name $entered_passwd , $id{$chosen_name}";
if($entered_passwd eq $id{$chosen_name}){
# print "Got it";
# generate new password
$new_psswd = &make_rno(5);
# report it to student
print "<br>Your password next time will be (note it down): $new_psswd";
$id{$last_name} = $new_psswd;
open ( STUD_FILE , "> $filename")||die"Can't open newfile:$!" ;#open to over-write!
for($j=1;$j<$nrec+1;$j++){
($last_name,$first_name,$password) = split(/+/,$safety_line[$j]);
if ($last_name eq $chosen_name){$password = $new_psswd};
$new_record = join('+',$last_name,$first_name,$password);
print STUD_FILE $new_record,"
";
# print "<br>",$j," ->",$new_record;
}
close (STUD_FILE);
# give clickable address for table of contents
$name = "$chosen_name".&make_rno(3);
print "<br><a href=main_menu.pl?name=$name>Entry to Main Menu</a>";
#
}
}
print $query->end_form;
print "<hr>
";
print $query->start_form;
print "Last Name = ",$query->textfield('new_student_last_name','',20,40);
print "First Name = ",$query->textfield('new_student_first_name','',20,40);
print $query->submit('sub2','New Student Registration');
if ($query->param('sub2')){
$new_passwd = &make_rno(5);
print "<br>Your password next time will be (note it down): $new_passwd";
$id{$last_name} = $new_psswd;
$new_name = $query->param('new_student_last_name');
$new_1name = $query->param('new_student_first_name');
open ( STUD_FILE , ">>$filename") ;#open to append!
$new_record = join('+',$new_name,$new_1name,$new_passwd);
print STUD_FILE $new_record,"
";
# print "<br>DEBUG",$new_record,"
";
close (STUD_FILE);
print "<br><a href=main_menu.pl?name=$new_name>Entry to Main Menu</a>";
}
print $query->end_form;
print "<hr>
";
print $query->start_form;
#this is the guest login
print <<EOF;
There are two parts to guest registration, one part (obviously optional)
involves writing in a
guestbook, the second part allows the actual access. I would prefer if you use
the guestbook, but, feel free to do what you like.
No matter what you choose, you need to place something into the box marked `Last Name = ' and you need to press the `Guest Registration' button to continue.
<br>
<a href="http://mmedia.ucc.uconn.edu/~cdavid/cgi-bin/book_guestbook.pl">Please register in the guestbook.</a><br>
EOF
print "Last Name = ",$query->textfield('guest_last_name','',20,40);
print $query->submit('sub3','Guest Registration');
if ($query->param('sub3')){
if ($query->param('guest_last_name') ne ''){
$name = $query->param('guest_last_name').'GUEST';
print "<br><a href=main_menu.pl?name=$name>Entry to Main Menu</a>";
}
}
print $query->end_form;
print $query->end_html;
#print "password = ",&make_rno(7);
# credit to shelden@spoke.law.cornell.edu
#
#srand;#this initializes the random number generator
# at the outset, while debugging, comment this out
#srand($$|time);#this initializes the random number generator
#$ascii_passwd = &make_rno(5);
#print $ascii_passwd;
sub make_rno{
srand($$|time);#this initializes the random number generator
local(@passset,$rnd_passwd,$randum_num);
local ($randum_num);
#since l = both el and one, do not use
#since 0 = both oh and zero, do not use
@passset = ('a'..'k','m'..'z','A'..'N','P'..'Z','2'..'9');
$rnd_passwd = "";
for ($i = 0; $i<$_[0];$i++){
$randum_num = int(rand($#passset+1));
$rnd_passwd .= @passset[$randum_num];
}
return $rnd_passwd;
}
To look at this code, we break it up into sections:
[ 1]use CGI;
[ 2]$query = new CGI;
[ 3]print $query->header;
[ 4]print $query->start_html('login_cgi.pl');
[ 5]print "This program reads the old student file, creates a scrolling list, and asks you to either choose one (i.e. old student) or input a name (i.e. new student).";
[ 6]
[ 7]#last_name+first_name+password, where '+' is the delimter
[ 8]$filename = "students_fall_96.dat";
[ 9]#now we abandon pseudo FORTRAN/BASIC/etc codes, and do it in Perl fashion
[10]$i = 1;
[11]open ( STUD_FILE , $filename) ;
[12] while(<STUD_FILE>){
[13] chop;
[14] if($_ ne ''){
[15] $line = $_;
[16] $safety_line[$i] = $line;
[17]# print "<br>DEBUG",$i,"->",$safety_line[$i];
[18] $i++;
[19] ($last_name,$first_name,$password) = split(/+/,$line);
[20] if($last_name ne ''){ $id{$last_name} = $password;}
[21] push(@last_name,$last_name);
[22]# print $id($last_name),"
";
[23] }
[24]}
[25]$i--;
[26]$nrec = $i;
[27]close(STUD_FILE);
The above code does (in Perl fashion) what we have done before, read the
existing file containing names and passwords.
Please note the following code omissions.
- We are not checking for the prior existence of the student file.
Good coding process should include this possibility, i.e., what happens
when the very first student accesses the file and it isn't there.
- We are not encrypting the passwords.
Perhaps it is a good time to note that security will become a more pressing
issue when Computer Assisted Testing becomes the normal method of testing.
Once that occurs, the mice will come out of their nests, nibbling at the
edges, searching for the chink in the armor which will allow them to cheat.
Sad as it is, one must recognize that this is coming, not possibly, definitely.
So, at some time, we will have to address the encryption problem.
But, that's for the future.
The following code reads a line from STUD_FILE, chops off the
carriage return at the end of each line, places it in $line, and then
[11]open ( STUD_FILE , $filename) ;
[12] while(<STUD_FILE>){
[13] chop;
[14] if($_ ne ''){
[15] $line = $_;
[16] $safety_line[$i] = $line;
[17]# print "<br>DEBUG",$i,"->",$safety_line[$i];
[18] $i++;
[19] ($last_name,$first_name,$password) = split(/+/,$line);
splits $line into its parts, $last_name, $first_name, and $password using
the split instruction.
Note that the `+' delimiter defined as the first argument in split is
the same one we chose to create the file orginally.
The rest of this loop now follows:
[20] if($last_name ne ''){ $id{$last_name} = $password;}
[21] push(@last_name,$last_name);
[22]# print $id($last_name),"
";
[23] }
[24]}
[25]$i--;
[26]$nrec = $i;
[27]close(STUD_FILE);
We define an ARRAY function `@id' whose elements `$id{some index}' are
the values of the passwords read from the student file.
Note that we only add a password to the file if the name is not blank.
[20] if($last_name ne ''){ $id{$last_name} = $password;}
We define an ARRAY function,`@last_name', and push the current $last_name
into the array, for future use.
(Note that line 22 is a commented out debug line.)
The STUD_FILE for test purposes now contains the following two lines:
stalin+joseph+XzezG
breznev+leonid+4ofre
and remember that you can not see the newline character `\n' at the end
of each line.
After the first read, $id{stalin} = XzezG.
That's the point of the array function.
After the second read, $id{breznev} = 4ofre.
Why do we need this?
Read on.
We are going to use three forms, with three `Submit' buttons, one for
students who have already been enrolled, like Stalin and Breznev, one for
students who are enrolling for the first time, and one for guests.
The first form follows:
[28]print $query->start_form;
[29]#print $query->radio_group('choice_of_login_mode',['from list',
[30]# 'adding new student'],'from list','true');
[31]print $query->scrolling_list('student_list', @last_name);
The commented out text is from earlier versions which you have already seen.
This time we use a scrolling list, the elements of which are the names of
all the students who have already enrolled.
Line 31 does it all, no matter how many students have already enrolled.
Isn't that marvelous.
[31]print $query->scrolling_list('student_list', @last_name);
Notice that we pass the ARRAY variable @last_name, which contains all
the (last) names so far enrolled.
By the way, if you are worried about twins, perhaps you want to change
statement 21 to include the first name also, perhaps something like:
[21] push(@last_name,$last_name.",".$first_name);
but if you do that, code further down will fail, so be careful.
[32]#if($query->param('sub1')){
[33]$msg = "Student Registration";
[34]print $query->submit('sub1',$msg);
My original code intended that the label on the submit button change
from the first occurence, to a "submit password" label on the second
occurence, but this turned out to be difficult, so I forgot about it
for the purposes of this text.
Anyway, line 34 could just as well have been written as:
[34]print $query->submit('sub1',"Student Registration");
The identity of this particular Submit button is 'sub1', and line 35
tests for whether or not that button has been pressed.
[35]if ($query->param('sub1')) {
and executes the bracketed code only if the Submit button was 'sub1'.
(There will be two other submit buttons showing up.)
[35]if ($query->param('sub1')) {
[36] $chosen_name = $query->param('student_list');
$chosen_name contains the value which the student clicked on.
This is how CGI.pm returns a value from a scrolling list.
[37] print "<br>Enter your password here:",$query->password_field('pswd','',5,8), " and resubmit by pressing `Student Registration' button.";
[38] $entered_passwd = $query->param('pswd');
[39]# print "<br>(DEBUG)$chosen_name $entered_passwd , $id{$chosen_name}";
For debugging purposes, it makes sense to print out the password in the file
so that you can enter it when the program requires, hence the commented out
line 39.
Line 37 presents a password_field which will not show as the student types,
contrary to a textfield.
Line 38 retrieves the value that the student typed in, and line 39 is a
commented out debug line which is self explanatory.
You will see that here (line 40) is where the button label might have been changed, but
that's for another day.
[40] if($entered_passwd eq $id{$chosen_name}){
[41]# print "Got it";
When I commented (above) about showing last and first names rather than just
last names, here is where care would have to be taken to make sure that
we looked up the student properly.
Anyway, line 40 is the key to identifying whether or not the student knew
the file's password value.
The $chosen_name indexes $id to find the file's value of the password,
and the statement compares the $entered_passwd to the file's value.
Line 41 is a commented out debugging statement of obvious utility.
[42]# generate new password
[43] $new_psswd = &make_rno(5);
This is our use of a subroutine (whose code is at the bottom of this listing).
We create a new password, as promised, and will record it into the file
so that each time the student uses the system, s/he gets a new password.
(If we were counting the number of times the student used the system, this
would be an appropriate place to increment the counter by 1 and write it into
the password file, separated by a `+', but that would entail adjusting
everything else we did (above), and I choose not to do it.)
[44]# report it to student
[45] print "<br>Your password next time will be (note it down): $new_psswd";
[46] $id{$last_name} = $new_psswd;
Line 46 changes the value which is currently in $id{$last_name} from
the value read to the new value.
[47] open ( STUD_FILE , "> $filename")||die"Can't open newfile:$!" ;#open to over-write!
[48] for($j=1;$j<$nrec+1;$j++){
[49] ($last_name,$first_name,$password) = split(/+/,$safety_line[$j]);
[50] if ($last_name eq $chosen_name){$password = $new_psswd};
[51] $new_record = join('+',$last_name,$first_name,$password);
[52] print STUD_FILE $new_record,"\n";
[53]# print "<br>",$j," ->",$new_record;
[54] }
[55] close (STUD_FILE);
Now we have written out the entire file, replacing the original file,
There is lots of code to consider.
[47] open ( STUD_FILE , "> $filename")||die"Can't open newfile:$!" ;#open to over-write!
Opening a file with a single > means that if the file exists, it will
be overwritten.
When debugging, one has to be careful, or one will overwrite a file improperly,
and then be unable to read that file during the next debugging cycle.
Therefore, when debugging, it is better to give the new output file a new name
and then copy it back to the old name if all is OK.
Then, change the program to overwrite.
The special variable `$!' contains the value of the last system error
which occured. This is written out in the `die' statement.
[48] for($j=1;$j<$nrec+1;$j++){
[54] }
FORTRAN programmers see a DO loop here.
This statement loops over the variable $j starting at a value of 1, and ending
at a value of $j<$nrec+1.
The ending value is gotten because the loop will increment $j as long as
the condition $j<$nrec+1 is true.
The incrementing is done after the curly bracket group is done.
[49] ($last_name,$first_name,$password) = split(/+/,$safety_line[$j]);
[50] if ($last_name eq $chosen_name){$password = $new_psswd};
[51] $new_record = join('+',$last_name,$first_name,$password);
[52] print STUD_FILE $new_record,"\n";
[53]# print "<br>",$j," ->",$new_record;
Line 49 takes each safety line and re-splits it into its components (we did this
before).
Line 50 checks each record, and updates the password of the chosen student.
Line 51 repacks the record into a single line, ready for writing to disk.
And line 52 writes the line, including a carriage return, to the disk.
Line 53 is a debug line which can be used to show you which lines are being
written to disk.
The last thing we do is to open the door for this student to see
the menu of available questions.
Notice that this student's name is being made longer with three random
numbers.
This will allow us to keep track of each time the student uses the system,
but you will have to wait to see how this is true.
Notice that we pass the name to `main_menu.pl' in a special manner, one which
is universal for this kind of work.
When we write `main_menu.pl' you will see how we retrieve the name, and
how we pass it to each question (in the same manner).
[56]# give clickable address for table of contents
[57] $name = "$chosen_name".&make_rno(3);
[58] print "<br><a href=main_menu.pl?name=$name>Entry to Main Menu</a>";
[59]#
[60] }
[61]}
[62]
[63]
[64]print $query->end_form;
This ends the first part of the exercise, where we have taken an existing
student and allowed him/her into the system provided s/he knows the existing
password.
If this is a new student, we want the student to give us a last and first
name, and we will assign a password for next time.
[66]print $query->start_form;
[67]print "Last Name = ",$query->textfield('new_student_last_name','',20,40);
[66]print "First Name = ",$query->textfield('new_student_first_name','',20,40);
[69]print $query->submit('sub2','New Student Registration');
The second submit button has a new identifier, so that we can if we wish
tell them apart.
[70]if ($query->param('sub2')){
[71] $new_passwd = &make_rno(5);
[72] print "<br>Your password next time will be (note it down): $new_passwd";
[73] $id{$last_name} = $new_psswd;
[74] $new_name = $query->param('new_student_last_name');
[75] $new_1name = $query->param('new_student_first_name');
[76]open ( STUD_FILE , ">>$filename") ;#open to append!
[77] $new_record = join('+',$new_name,$new_1name,$new_passwd);
[78] print STUD_FILE $new_record,"
";
[79]# print "<br>DEBUG",$new_record,"
";
[80]close (STUD_FILE);
[81]print "<br><a href=main_menu.pl?name=$new_name>Entry to Main Menu</a>";
[82]}
[83]print $query->end_form;
[84]print "<hr>
";
This code is virtually identical to the code (above), except that this time
we append the new student to the existing file, i.e., we use
`>>' so that instead of overwriting the file, we just add one more
record, the new student's record.
[85]print $query->start_form;
[86]#this is the guest login
[87]print <<EOF;
[88]There are two parts to guest registration, one part (obviously optional)
[89]involves writing in a
[90]guestbook, the second part allows the actual access. I would prefer if you use
[91]the guestbook, but, feel free to do what you like.
[92]No matter what you choose, you need to place something into the box marked `Last Name = ' and you need to press the `Guest Registration' button to continue.
[93]<br>
[94]<a href="http://mmedia.ucc.uconn.edu/~cdavid/cgi-bin/book_guestbook.pl">Please register in the guestbook.</a><br>
[95]EOF
[96]print "Last Name = ",$query->textfield('guest_last_name','',20,40);
[97]print $query->submit('sub3','Guest Registration');
[98]if ($query->param('sub3')){
[99] if ($query->param('guest_last_name') ne ''){
[100] $name = $query->param('guest_last_name').'GUEST';
[101] print "<br><a href=main_menu.pl?name=$name>Entry to Main Menu</a>";
[102] }
[103]}
[104]print $query->end_form;
[105]print $query->end_html;
[106]
There is nothing new (above) from the point of view of Perl. We are
going to allow this guest in whether or not s/he signs the guestbook, but
in either case, we will pass a name indicative of the GUEST status to the main_menu,
so that when we later review the log files, we can see the difference between
guests and students.
Next comes the password subroutine.
We have already discussed how this works, both in a main program
and as a subroutine.
[107]
[108]
[109]
[110]#print "password = ",&make_rno(7);
[111]# credit to shelden@spoke.law.cornell.edu
[112]#
[113]#srand;#this initializes the random number generator
[114]# at the outset, while debugging, comment this out
[115]#srand($$|time);#this initializes the random number generator
[116]#$ascii_passwd = &make_rno(5);
[117]#print $ascii_passwd;
[118]sub make_rno{
[119] srand($$|time);#this initializes the random number generator
[120] local(@passset,$rnd_passwd,$randum_num);
[121] local ($randum_num);
[122] #since l = both el and one, do not use
[123] #since 0 = both oh and zero, do not use
[124] @passset = ('a'..'k','m'..'z','A'..'N','P'..'Z','2'..'9');
[125] $rnd_passwd = "";
[126] for ($i = 0; $i<$_[0];$i++){
[127] $randum_num = int(rand($#passset+1));
[128] $rnd_passwd .= @passset[$randum_num];
[129] }
[130] return $rnd_passwd;
[131]}
and that's all there is, folks.