Innlogging
Det er ofte et problem å ordne logikken i slike innloggingsprosedyrer. Det oppstår en rekke situasjoner som fører til dialogsekvenser som det kan være vanskelig å ha klart i hodet mens en programmerer. Figuren nedenfor illustrerer mulige sekvenser i denne innloggingsprosedyren. Figuren tjener som dokumentasjon og som støtte under programmeringen.
De enkelte fragmentene som skal vises er merket med F for Fragment. Alle aksjonene som respons på brukervalg er merket med J, for Job. for å holde orden på dette er alle fragmentene samlet i en fil, der hvert fragment er merket slik at det kan plukkes ut av et skript. Hver job tilsvarer en funksjon med samme navn i en Javascriptfil.
Fila som inneholder alle fragmentene er slik: registerfragments.html. Fordelen med å ha denne som en html-fil er at den kan redigeres og inspiseres og delvis testes i en nettleser. Når skriptet kannibaliserer denne og henter det fragmentet det trenger så får vi det vi forventer.
De jobbene som skal gjøres er samlet på en javascriptfil:
// ajax stuff
var registerskript='register.py'
// relies on prototype.js
function getIt(params,targetNodeId)
{
if(params.length > 0)
params='?'+params;
new Ajax.Request(registerskript,
{method:'post',
parameters:params,
onSuccess:function(transport){
var T=transport.responseText;
$(targetNodeId).update(T);
},
onFailure:function(){$(targetNodeId).update("Could not access content")}
});
}
//------------------------------------------------------
// jobs according to documented statediagram
// fetching the initial login
// has password, no password, forgotten password as options
function job0() // job0
{
var userId=readCookie('lastUser');
if (userId==null)
params='job=job0';
else
params='job=job5&userId='+userId;
getIt(params,'entry');
}
// Sending normal log in with known password, job1
// non-blank userName and userPwd
function job1(form)
{
userName=form.userName.value;
userPwd=form.userPwd.value;
ok=true;
document.getElementById('emptynameF1').innerHTML='';
document.getElementById('emptypwdF1').innerHTML='';
if(userName.length ==0){
ok=false
document.getElementById('emptynameF1').innerHTML='fyll ut !';
}
if(userPwd.length ==0){
ok=false
document.getElementById('emptypwdF1').innerHTML='fyll ut !';
}
if(!ok)
return;
params='job=job1&userName='+escape(userName)
+'&userPwd='+escape(userPwd);
getIt(params,'entry');
}
// User ha forgotten his password. From F1
// userName may be non-blank
function job2(form)
{
userName=form.userName.value;
if(userName.length ==0)
userName='no_value';
params='job=job2&userName='+escape(userName);
getIt(params,'entry');
}
// New user. userName may be non-blank
function job3(form)
{
userName=form.userName.value;
if(userName.length ==0)
userName='no_value';
getIt('','entry');
}
// user has filled inn a password form, and want to
// be reckognized
function job4(form)
{
params='job=job4';
userName=form.userName.value;
document.getElementById('emptynameF3').innerHTML='';
if(userName.length ==0)
{
document.getElementById('emptynameF3').innerHTML='fyll ut !';
return
}
params+='&userName='+escape(userName);
params+='&userBirth='+escape(form.userBirth.value);
params+='&userPostnr='+escape(form.userPostnr.value);
params+='&userLucky='+escape(form.userLucky.value);
getIt(params,'entry');
}
// A simple confirmation that user is to be let in,from F5
function job5(form)
{
userId=form.userId.value;
params='job=job5&userId='+userId;
getIt(params,'entry');
}
// user has filled inn a password form, and want to register
function job6(form)
{
userName=form.userName.value;
if(userName.length ==0){
document.getElementById('emptynamex').innerHTML='fyll ut !';
return;
}
userPwd1=form.userPwd41.value;
userPwd2=form.userPwd42.value;
if (userPwd1.length==0){
document.getElementById('emptyPwd41').innerHTML='fyll ut !';
return;
}
if (userPwd2.length==0){
document.getElementById('emptyPwd42').innerHTML='fyll ut !';
return;
}
if (userPwd1 != userPwd2){
document.getElementById('emptyPwd42').innerHTML='de er ikke like';
return;
}
params='job=job6';
params+='&userName='+escape(userName);
params+='&userPwd='+escape(userPwd1);
params+='&userBirth='+escape(form.userBirth.value);
params+='&userPostnr='+escape(form.userPostnr.value);
params+='&userLucky='+escape(form.userLucky.value);
getIt(params,'entry');
}
// User cancels entryattempt
function job7()
{
params='job=job7';
getIt(params,'entry');
}
// User is not found
function job8()
{
params='job=job8';
getIt(params,'entry');
}
// try registering new user again
function job9()
{
params='job=job9';
getIt(params,'entry');
}
// logout
function job10()
{
// need path and domain to be able to delete ?
// set in the pythonskript: register.py, prepareCookie
eraseCookie('lastUser','/','it.hiof.no');
window.location.reload();
}
Vi bruker en cookie for at brukeren skal slippe å registrere seg hver gang han går inn på siden, f.eks ved en reload. Cookien settes i serverskriptet, se nedenfor, og den fjernes i Javascriptkoden ved utlogging eller når nettleseren avslutter. Setting og fjerning av cookies kan gjøres slik:
//************* cookiestuff *******************
// There are numerous sources for cookie-kode on the net
// For instance:
// http://www.netspade.com/articles/2005/11/16/javascript-cookies/
function createCookie(name,value,minutes) {
if (minutes) {
var date = new Date();
date.setTime(date.getTime()+(minutes*60*1000));
var expires = "; expires="+date.toGMTString();
}
else
var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0)
return unescape(c.substring(nameEQ.length,c.length));
}
return null;
}
function eraseCookie(name,path,domain) {
if(readCookie(name)){
document.cookie = name + "=" +
((path) ? "; path=" + path : "") +
((domain) ? "; domain=" + domain : "") +
"; expires=Thu, 01-Jan-70 00:00:01 GMT";
}
}
Denne javascriptfila gjør henvendelser til et pythonskript som leverer de fragmentene vi ønsker. Navnet på jobben som skal gjøres viderføres til Pythonskriptet slik at vi kan holde styr på programmeringen i henhold til planen. Pythonskriptet er slik:
#! /usr/bin/python2.5
import cgi,smtplib,datastorage,Cookie
import cgitb; cgitb.enable()
# -*- coding: cp1252 -*-
"""
Handles all phases in a pwd registration
"""
# fragments are extracted from this file:
FRAGMENT_FILENAME='registerfragments.html'
#-----------------------------------------
# error messages
GET_FILE_ERROR='kan ikke lese fil: %s'
GET_FRAGMENT_ERROR='finner ikke fragment: %s'
#-----------------------------------------
# extract fragments from file
# fragmentnames we want is a list of fragments
def extractFragment(filename,fragmentnames):
T=''
try:
f=open(filename,'r')
T=f.read()
f.close()
except:
return GET_FILE_ERROR%filename
Tcopy=T
result=''
for fragmentname in fragmentnames:
T=Tcopy
leftpar='<!-- '+fragmentname+ ' -->'
rightpar='<!-- /'+fragmentname+ ' -->'
pos1=T.find(leftpar)
if pos1 == -1:
return GET_FRAGMENT_ERROR%fragmentname
T=T[pos1+len(leftpar):]
pos2=T.find(rightpar)
if pos2 == -1:
return GET_FRAGMENT_ERROR%fragmentname
result=result+T[:pos2]
return result
#----------------------------------------
# Find password for a user with id userId
# return None if not available
def getPassword(userId):
return datastorage.getUserPassword(userId)
#-----------------------------------------
# Find userName given a userId
# return None if not found
def getUserName(userId):
return datastorage.getUserName(userId)
#----------------------------------------
# register user
# Complete registration from form
# expect to find:userName,userEmail,userBirth,userPostnr,userLucky
# the latter three may be empty or non-existant
def registerUser(dataList):
# userName,userPwd,userBirth,userPostnr,userLucky
if dataList[0]=='no_value':
return None
if dataList[1]=='no_value':
return None
datastorage.registerUser(dataList)
userId=datastorage.getUserID(dataList[0],dataList[1])
return userId
#----------------------------------------
# user has filled out a help-me-I-have-forgotten-passwd form
# form contains submitted data
# return True if we find a (good enough) match on submitted controldata
# False otherwise
# expect to find:userName,userEmail,userBirth,userPostnr,userLucky
# the latter three may be empty or non-existant
def controlDataSet(form):
# unpack and see if it matches
userName='no_value'
userPwd='no_value'
userBirth='no_value'
userPostnr='no_value'
userLucky='no_value'
userPwd='no_value' # not supplied and stays no_value
if form.has_key('userName'):
userName=form['userName'].value
if form.has_key('userBirth'):
userBirth=form['userBirth'].value
if form.has_key('userPostnr'):
userPostnr=form['userPostnr'].value
if form.has_key('userLucky'):
userLucky=form['userLucky'].value
# expect None if no match
userId=datastorage.findMatch(\
[userName,userPwd,userBirth,userPostnr,userLucky])
return userId
#----------------------------------------
# user has submitted userName and userPwd
# is it ok to let him in ?
# return userId if so is the case
# return None otherwise
def getUserId(userName,userPwd):
# unpack and see if user is registered with the given passwd
result=datastorage.getUserID(userName,userPwd)
if result == None:
return None
return result
#----------------------------------------
# prepare a cookie:lastUser
def prepareCookie(userName,userId):
c=Cookie.SimpleCookie()
c['lastUser']=userId
# in seconds or default is end of browser session
#c['lastUser']['max-age']=60*120
c['lastUser']['path']='/'
c['lastUser']['domain']='.ia.hiof.no'
cookieText=c.output()
return cookieText
#-------------------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=iso-8859-1\n'
result= '\n'
job='emergency_exit'
if form.has_key('job'):
job=form['job'].value
if job=='job0':
# set up the intial form for name and password entry
# this form also includes single lines for
# forgotten password and want passwrd (new user)
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
print result
elif job=='job1':
# normal login attempt with known passwd
# we expect to find: userName and userPwd
# userPwd is unpacked in getUserId
# Let him in or return to initial situation (job0)
userName='no_value'
if form.has_key('userName'):
userName=form['userName'].value
userPwd='no_value'
if form.has_key('userPwd'):
userPwd=form['userPwd'].value
userId=getUserId(userName,userPwd)
if userId != None:
result+=extractFragment(FRAGMENT_FILENAME,['F6'])%\
(userId,userName)
print prepareCookie(userName,userId)
print result
else:
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F2'])%\
userName+'</div>'
print result
elif job=='job2':
# give user a chance to fill in controldata
# expect userName, may be no_value
userName='no_value'
if form.has_key('userName'):
userName=form['userName'].value
if userName=='no_value':
userName=''
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F3'])%\
userName+'</div>'
print result
elif job=='job3':
# start a New user form
# expect userName, may be no_value
userName='no_value'
if form.has_key('userName'):
userName=form['userName'].value
if userName=='no_value':
userName=''
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F4'])%\
(userName)+'</div>'
print result
elif job=='job4':
# control data filled in by a forgetfull user
# expect to find:userName,userEmail,userBirth,userPostnr,userLucky
# the latter three may be empty or non-existant
userName='no_value'
if form.has_key('userName'):
userName=form['userName'].value
userId=controlDataSet(form)
if userId != None:
userPwd=getPassword(userId)
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F5'])%\
(userId,userName,userName,userPwd)+'</div>'
else:
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F2'])%\
userName+'</div>'
print result
elif job=='job5':
#transport us in
# expect to find userId
userId='no_value'
if form.has_key('userId'):
userId=form['userId'].value
userName=getUserName(userId)
if userName==None:
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
else:
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F6'])%\
(userId,userName)+'</div>'
print prepareCookie(userName,userId)
print result
elif job=='job6':
# receive data from password request form
# expect to find:userName,userPwd,userBirth,userPostnr,userLucky
# the latter three may be empty or non-existant
# register everything
userName='no_value'
userPwd='no_value'
userBirth='no_value'
userPostnr='no_value'
userLucky='no_value'
if form.has_key('userName'):
userName=form['userName'].value
if form.has_key('userPwd'):
userPwd=form['userPwd'].value
if form.has_key('userBirth'):
userBirth=form['userBirth'].value
if form.has_key('userPostnr'):
userPostnr=form['userPostnr'].value
if form.has_key('userLucky'):
userLucky=form['userLucky'].value
userId= registerUser([userName,userPwd,userBirth,userPostnr,userLucky])
if userId != None:
result+= '<div>'+extractFragment(FRAGMENT_FILENAME,['F6'])%\
(userId,userName)+'</div>'
print prepareCookie(userName,userId)
else:
result+='<div>'+ extractFragment(FRAGMENT_FILENAME,['F7'])+'</div>'
print result
elif job=='job7':
# transport to initial form
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
print result
elif job=='job8':
# transport to initial form
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F1'])+'</div>'
print result
elif job=='job9':
# transport to registration form
result+='<div>'+extractFragment(FRAGMENT_FILENAME,['F4'])%''+'</div>'
print result
else: # unknown job
print '\n<div> not implemented: %s</div>'%job
Selve datalagringen er skilt ut i en egen, utbyttbar, modul som ser slik ut:
#! /usr/bin/python2.5
import cgi,MySQLdb,random
import cgitb; cgitb.enable()
# -*- coding: cp1252 -*-
"""
Handles data access for a std registration.
Must handle these tasks:
- Initialize the database table with a dummy user
initDatabase()
- Find password for a user, given the ID:
getUserPassword(userId)
- Find name for a user, given id:
getUserName(userId)
- Find id for a user, given name and password:
getUserID(userName,userPwd)
- Find id of a user, given all other data:
findMatch(test)
- Register a user, give all data(except id):
registerUser(dl)
Using database with table with structure as shown below
"""
#----------------------------------------------
# SQL - expressions
SQL_USE_DB="""use diverse;"""
SQL_DROP_TABLE="""drop table if exists regusers;"""
SQL_INIT_TABLE="""
create table regusers(
userId INT PRIMARY KEY AUTO_INCREMENT,
userName VARCHAR(35),
userPwd VARCHAR(10),
userBirth VARCHAR(10),
userPostnr VARCHAR(10),
userLucky VARCHAR(10)
);
"""
SQL_USER_WITH_NAME="""select * from regusers where userName='%s';"""
SQL_USER_WITH_ID="""select userName from regusers where userId='%s';"""
SQL_USER_PWD="""select userPwd from regusers where userId='%s';"""
SQL_USER_ID="""select userId from regusers
where userName='%s' and userPwd='%s';"""
SQL_INSERT_USER_DATA="""
insert into regusers(userName,userPwd,userBirth,userPostnr,userLucky)
values('%s','%s','%s','%s','%s'); """
#----------------------------------------------
# connect and execute a sql-request
def connectAndExecute(sql):
try:
myBase=MySQLdb.connect(host='frigg.hiof.no',
user='borres',
passwd='hemmelig',
db='diverse')
myTab=myBase.cursor()
sql=sql.decode('latin1')
myTab.execute(sql)
myBase.commit()
myBase.close()
return myTab.fetchall()
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
return None
#----------------------------------------
# init database with a dummy user
def initDatabase():
connectAndExecute(SQL_USE_DB)
connectAndExecute(SQL_DROP_TABLE)
connectAndExecute(SQL_INIT_TABLE)
connectAndExecute(SQL_INSERT_USER_DATA%('ole','gallas','46','1784','7'))
#-----------------------------------------
# get registered password for a user
def getUserPassword(userId):
result=connectAndExecute(SQL_USER_PWD%userId)
if len(result)==0:
return None
return str(result[0][0])
#----------------------------------------
# Get unique ID for a user, based on name and pwd
def getUserID(userName,userPwd):
result=connectAndExecute(SQL_USER_ID%(userName,userPwd))
if len(result) != 1:
return None
else:
return str(result[0][0])
#----------------------------------------
# Get userName , based on userId
def getUserName(userId):
result=connectAndExecute(SQL_USER_WITH_ID%(userId))
if len(result) != 1:
return None
else:
return str(result[0][0])
#--------------------------------------------
# get a users dataset
def getUserData(userName):
result=connectAndExecute(SQL_USER_WITH_NAME%userName)
if len(result) == 0:
return []
return result
#------------------------------------------
# check if this set matches someone in th database
def findMatch(test):
# test: userName,userPwd,userBirth,userPostnr,userLucky
usrList=getUserData(test[0])
found=False
for usr in usrList:
#usr: userId,userName,userPwd,userBirth,userPostnr,userLucky
# userPwd is index 2, pass it
found=True
for ix in range(2,5):
if test[ix]!=usr[ix+1]:
found=False
if found:
return usr[0] # userid
return None
#----------------------------------------
# set users datasett
def registerUser(dl):
if len(dl) != 5:
return None;
# save no_value as missing
# makes comparing easier (mismatch and no value)
for ix in range(0,len(dl)):
if dl[ix] == 'no_value':
dl[ix] = 'missing'
connectAndExecute(SQL_INSERT_USER_DATA%(dl[0],dl[1],dl[2],dl[3],dl[4]))