Backend() Class Introduction
This class is a template (A.K.A. "virtual base class"?) for how the auth.Sessions Backends work. This class is never meant to be used directly by users, except for creation of a new Backend (for talking to a currently-unsupported database, for example).
The description of methods below is appropriate for getting a feel for how the backends work, and should be read in addition to any Backend-specific documentation (which in general will only cover how it differs from this).
Users who wish to build new Backends should see "tests/test009/testbe-fs" in the original JOTWeb source file for a comprehensive test which their backend should be able to pass.
Backend*() General Usage
The Backends store informaiton on the last session and user that were accessed. Methods like "userdel()" do not take any arguments, because they operate on the last retrieved user. So, to delete user "bob", you would do "backend.userget('bob'); backend.userdel()". Of course, you would want to check "backend.userget()" to ensure that it succeeded before deleting the user.
In the documentation on Methods, below, the setting and clearing of these values are documented in the Side-Effects section.
Backend*() User and Session Objects
Many backend routines return session and/or user objects. These objects are dictionaries, however only the elements marked as "Persistant" below should be modified. Changing items other than those marked as persistant will have undefined results.
The "payload" item is a dictionary, initially empty, which you may store your own program-specific data in. This dictionary is pickled and stored into the backend storage system, so it may contain arbitrary data. However, it's size may be limited by the backend. All backends must be able to store at least 1024 bytes of data, after pickling. Remember to call the "sessionsave()" and "usersave()" methods after updating the payload.
User objects have the following elements:
- ackkey -- (boolean) (Persistant) The value set in this field can be used in combination with the username to enable the account using the ackverify() method.
- createddate -- (integer) Results of Python time.time() when record was created.
- cryptpasswd -- (string) (Persistant) Crypted version of password for account.
- enabled -- (boolean) (Persistant) Is this user account enabled. If False, all user verification attempts will fail.
- lasthit -- (integer or None) If user has validated a session, this is the results of the Python time.time() call when the last session was validated.
- lastlogin -- (integer or None) If user has logged in, this is the result of the Python time.time() call when the last login occured.
- payload -- (Dictionary) (Persistant) Dictionary for user of the Backend class to store information in.
- username -- (string) Name of user account. Should be as user entered the value, any quoting or unquoting should be done by the backend automatically.
Session objects have the following elements:
- createddate -- (integer) Results of Python time.time() when record was created.
- cryptpasswd -- (string) Crypted version of password for account. This is stored in the session so that a password change can be detected and cause the session to expire.
- expires -- (integer or None) If not none, time (as represented by the Python time.time() call) after which the session will no longer be valid.
- expiresecs -- (integer or None) If None, session never expires. Otherwise, specifies the length of time since the last hit for which the session will be valid. After that number of seconds, the session will expire.
- key -- (string) Session key, the unique key which identifies the session.
- payload -- (Dictionary) Dictionary for user of the Backend class to store information in.
- username -- (string) Name of user account. Should be as user entered the value, any quoting or unquoting should be done by the backend automatically.
Backend Methods
genSessionKey()
Arguments: None
Returns: (string) A random string of characters.
Side-effects: None.
Exceptions: None.
Description: Returns a straing of random characters, suitable for use as a session key. This is most likely to be used by users who are writing new Backend classes.
genAckKey()
Arguments: None
Returns: (string) A random string of characters.
Side-effects: None.
Exceptions: None.
Description: Returns a straing of random characters, suitable for use as an ack key. This is most likely to be used by users who are writing new Backend classes.
Backend() Methods
ackverify(username, ackId)
Arguments:
- username -- (string) Name of user to verify.
- ackId -- (string) Acknowledgement identifier.
Returns: True if user/ackId combination is valid, False otherwise.
Side-effects: Clears stored user and session information, sets stored user on success. Clears "ackkey" field of user record on success.
Exceptions: Does not directly raise any exceptions, instead returning False.
Description: This code verifies a given username and ackId combination. If the user or ackId is invalid, the method returns False (as opposed to raising an exception)
sessionadd(username, expireSecs = None, key = None)
Arguments:
- username -- (string) Name of user to generate a new session for.
- expireSecs -- (integer, default=None) Number of seconds session is valid for, or None if session never expires.
- key -- (string, default=None) If set, specifies a key to be used. If that session key already exists, it is overwritten. If not specified, a new unique session key will be generated.
Returns: Session dictionary object.
Side-effects: Clears stored session information, resets it on success.
Exceptions: None generated directly.
Description: Adds a session for the specified user.
sessiondel()
Arguments:
Returns: None
Side-effects: Clears stored session information.
Exceptions: Raises KeyError if session key does not exist.
Description: Deletes the current session, including all payload and associated data. This session is no longer valid for authentication.
sessionget(key)
Arguments:
- key -- (string) Session key (as returned in the "key" element of the dictionary returned by "sessionadd()".
Returns: Session dictionary object.
Side-effects: Clears stored user and session information, sets stored session information if successful.
Exceptions: Raises KeyError if session does not exist.
Description: Retrieves the session data associated with the specified session.
sessionsave()
Arguments:
Returns: None
Side-effects: None.
Exceptions: Raises ValueError if there is no stored session.
Description: Saves the payload data for the stored session to persistant storage. The payload is a Python dictionary containing arbitrary key/value pairs (the full payload will be pickled into the backend).
sessionverify(key)
Arguments:
- key -- (string) Session key to verify for validity.
Returns: Returns a tuple of the session and user dictionary objects, or a tuple of (False, False) on failure.
Side-effects: Clears stored user and session information, setting them to the corresponding values if successful.
Exceptions: Does not directly raise any exceptions, instead returning False.
Description: Looks up the specified session, checking that it has not expired nor has it's password been changed or the user deleted.
useradd(username, cryptpasswd = None, passwd = None, createEnabled = True, generateAck = False)
Arguments:
- username -- (string) Name of user to add.
- cryptpasswd -- (string, default=None) If specified, the encrypted version of the user's password (see jotwebutils.cryptpasswd()).
- passwd -- (string, default=None) If specified, the plain-text password, which will be encrypted internally.
- createEnabled -- (boolean, default=True) When the account is created, are logins allowed? This would usually be set to False if using generateAck, for verifying user via an Acknowledgement handshake.
- generateAck -- (boolean, default=False) If true, an "ackkey" is stored with the user record. This is usually used when createEnabled is False, so that the user can be acknowledged via an e-mail or other means.
Returns: User data dictionary.
Side-effects: Clears existing session and user information, on success the stored user will reflect the current user.
Exceptions: KeyError if user already exists.
Description: Adds the specified user. If "cryptpasswd" is specified, that crypted password is used. Otherwise, if "passwd" is set, it is crypted with MD5 enabled and used as the crypted password.
If cryptpasswd is specified, it will be used in preference to passwd. If neither "cryptpasswd" or "passwd" are set, then the account is set up such that no password will work.
userdel()
Arguments: None
Returns: None
Side-effects: Clears stored user and session information.
Exceptions: Raises KeyError if user does not exist, and raises ValueError if no user was previously selected.
Description: Deletes the user the cursor is on. This is the user that was the result of the last "userget()" or other operation whic has the side-effect of updating the stored user.
userget(username)
Arguments:
- username -- (string) Name of user to look up.
Returns: User dictionary object.
Side-effects: Clears stored user and session information, on successful return sets the stored user.
Exceptions: Raises KeyError if user does not exist.
Description: Looks up the specified user, returning user dictionary information on success.
usersave()
Arguments: None
Returns: None
Side-effects: None.
Exceptions: Raises ValueError if there is no stored user.
Description: Saves the payload data for the stored user to persistant storage. The payload is a Python dictionary containing arbitrary key/value pairs (the full payload will be pickled into the backend).
userverify(username, passwd, updateLogin = True)
Arguments:
- username -- (string) Name of user to verify.
- passwd -- (string) Plain-text password for authentication.
- updateLogin -- (integer, default=True) If True, updates the "lastlogin" portion of user record.
Returns: True if user/passwd combination is valid, False otherwise.
Side-effects: Clears stored user and session information, sets stored user on success.
Exceptions: Does not directly raise any exceptions, instead returning False.
Description: This code verifies a given username and password combination. If the user or password is invalid, the method returns False (as opposed to raising an exception)
Example
These examples should work with any of the backends. In these examples, we are using the BackendFilesystem for demonstration.
Python Example: Adding a new user and session
from jotweb.auth import Sessions
sessionDir = '/tmp/sessiondir'
be = Sessions.BackendFilesystem(sessionDir)
# add a user
user = be.useradd('jafo', passwd = 'test1234')
user['payload']['mydata'] = 'Test data stored in user'
be.usersave()
# add a session
session = be.sessionadd('jafo')
session['payload']['mysessiondata'] = 'Test data stored with session.'
be.sessionsave()
# validate user
if be.userverify('jafo', 'test1234'):
print 'User validated successfully'
if not be.userverify('jafo', 'jafo'):
print 'User didn't verify when we used the wrong password.'
# validate session
newsession, newuser = be.sessionget(session['key'])
if newsession:
print 'Session validated successfully'
# delete session
be.sessiondel()
# look up and then delete user
be.userget('jafo')
be.userdel()HTML Form for Adding a new user:
Usually, you would never directly call the Backends directly from TAL, but below is an example of using input.SimpleForm.FormHandler() and the auth.Sessions.BackendFilesystem() code to create a form which allows adding users.
Create a file called "newuserform.html", which contains the form HTML:
<form action="adduserform.html">
Username: <input type="text" name="UserName" /><br />
Password: <input type="password" name="Password" /><br />
Password (Again): <input type="password" name="PasswordAgain" /><br />
<input type="submit" value="Create User" />
</form>Create a file called "adduserform.html", which contains the HTML and code for adding a new user:
<div tal:define="form adduserform/formvdt">
<div tal:condition="not:form/geterrors">
<!-- Form verification successful -->
<div tal:define="result adduserform/adduser">
<div tal:condition="not:result/failure">
<!-- User created successfully -->
<h1><font color="#ff0000">Added User</font></h1>
User added successfully.
</div>
<div tal:condition="result/failure">
<!-- User creation failed -->
<h1><font color="#ff0000">Adding User Failed</font></h1>
More detailed information: <span
tal:replace="result/reason">Failure Reason</span>
</div>
</div>
</div>
<div tal:condition="form/geterrors">
<!-- Form had errors, display them -->
<h1><font color="#ff0000">Form Errors</font></h1>
<ul>
<li tal:repeat="value form/geterrors" tal:content="value">Error</li>
</ul>
</div>
</div>Finally, make the Python code for handling the above, create "adduserform.py":
from jotweb.auth.Sessions import BackendFilesystem from jotweb.input import SimpleForm def formvdt(): 'Validate form data.' form = SimpleForm.FormHandler() if form.vdt_required('UserName'): form.cvt_makelowercase('UserName') # conver to lower-case form.vdt_length('UserName', 8, 3) # must have length of 3 to 8 form.vdt_login('UserName') # is a unix-like login name if form.vdt_required('Password'): form.vdt_length('Password', 50, 3) # must have length of 3 to 50 form.vdt_passwd('Password') form.vdt_equal('Password', 'PasswordAgain') def adduser(): '''Add user.''' form = SimpleForm.FormHandler() be = BackendFilesystem('/tmp/sessiondata') try: be.adduser(form['UserName'], passwd = form['Password']) except Exception, e: return({ 'failure' : 1, reason : 'Received exception "%s"' % str(e) }) return({ 'success' : 1 })