Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

authentication framework for web.py

branch: example_debate

This branch is 177 commits ahead and 6 commits behind master

Fetching latest commit…

Cannot retrieve the latest commit at this time

README
================================================================
authentication.py: user authentication and management for web.py
================================================================

The goal of this project is to provide a ready-to-use microframework for
creating and managing users, secure storage of authentication data, and
permission management for your `web.py <http://www.webpy.org/>`_ site.

Dependencies
============

* web.py (obviously)
* nose (for running tests only)

Project status
==============

This project is currently in heavy development. Most facilities either don't
work, or are not even designed yet. We have scheduled the release of 0.1 with
fully functional user management during January. Here is the brief roadmap:

* v0.1 (Jan 2010): fully functional user management
* v0.2 (Feb 2010): fully functional permissions management
* v0.3 (Feb 2010): fully functional group management

Each of the version will add one module to the project. User management is in
the ``auth`` module, which is curretly being worked on. Permissions management
will be pu in ``perm`` module, and groups will reside in ``group`` module.

Before using authentication.py
==============================

The first thing you need to do is tell authentication.py which database it will
be using. To do this, add ``authdb`` key to ``web.config``. Here's an example
using PostgreSQL::

   import web
   somedb = web.database(dbn='postgres', db='my_app_db', user='postgres')
   web.config.authdb = somedb

You can also have a separate database just for authentication (e.g, if you want
to use a common authentication database between different apps). Just assign
wahtever database you want to use to ``web.config.authdb``.

If you want to take advantage of messaging facilities, you also need to define
a ``web.config.authmail`` key, and assign it a dictionary of options::

   import web
   web.config.authmail = {'sender': 'default.sender@yoursite.com',
                          'activation_subject': 'Activate your account',
                          'reset_subject': 'Your password has been reset',
                          'delete_subject': Your account was removed',
                          'suspend_subject': 'Your account is suspended'}

Defaults for the subject messages are:

* ``activation_subject``: *Account activation*
* ``reset_subject``: *Password reset*
* ``delete_subject``: *Account removed*
* ``suspend_subject``: *Account suspended*

If you are happy with these defaults, you don't have to include them in the
configuration dictionary. Sender is still required.

User object
===========

Creating a new user
-------------------

To create a user, you can use the ``User`` class from the
``authenticationpy.auth`` module. Before actually creating a user, you need to
specify the three required properties::

   >>> from authenticationpy.auth import User
   >>> user = User(username='myuser'
   ...             email='user@someserver.com')
   >>> user.password = 'clear-text password'
   >>> user.create()

A user account is not atomatically activated after creation. The ``activate``
method must be called on the instance at some point to activate it.

Creating a user with activation e-mail
--------------------------------------

If you want to send out an activation e-mail, you can do so by specifying the
activation message::

   >>> from authenticationpy.auth import User
   >>> user = User(username="myuser",
   ...            email="user@someserver.com")
   >>> user.password = 'clear-text password'
   >>> user.create(message="""
                   Please activate your account at
                   http://mysite.com/activate/$url
                   """)

The format of the activation message is arbitrary, and you can use any 
template you like. The template variables available for customizing the 
message dynamically are:

* ``$url``: activation URL suffix that is generated automatically
* ``$username``: username
* ``$email``: the e-mail address used for creating the user
* ``$password``: the clear-text password

Note that the clear-text password is only stored in memory, and cannot be
retrieved later. If you want to send activation e-mails more than once, do not
include the password in your template. If the clear-text password is lost, the
template variable will be replaced with an empty string.

Setting activation information and obtaining the action code
------------------------------------------------------------

If you have your own system for notifying users about activation, and then
requesting activation action, you can use the ``set_activation`` method to set
the user account to an 'activatable' state. Here's an example::

   >>> from authenticationpy.auth import User
   >>> user = User(username='myuser',
   ...             email='user@someserver.com')
   >>> user.password = 'clear-text password'
   >>> code = user.set_activation()
   >>> user.create()

In the above example, the ``code`` variable is assigned an activation code that
is generated by the ``set_activation`` method. This code is the same one used
for the activation URL, and it's a SHA-256 hexdigest.

Finding a user account by activation code
-----------------------------------------

This does not strictly apply to activation. It also applies to all cases where
you may be using the action code (such as account removal confirmation, or
password reset). To get a user account by action (activation) code, you can use
the ``get_user_by_act_code`` class method. Here is an example::

   >>> from authenticationpy.auth import User
   >>> code = some_code # a SHA-256 hexdigest
   >>> user = User.get_user_by_act_code(code)
   # do something with the user account

Testing the timeliness of user action
-------------------------------------

Once a user clicks on the activation link (in fact, this applies to all cases
where you are using the action code for confirming user action), you know the
code, and you can also get the associated user account. If you don't impose any
deadlines for user action, you may proceed to activate the account, or any
other action that was confirmed. However, if you do impose deadlines, there is
a convenience method that you can use to test if user action was timely.

To test the timeliness of user actions, you can call the
``is_interaction_timely`` method on any ``User`` instance. ::

   >>> import datetime
   >>> from authenticationpy.auth import User
   >>> code = some_code # a SHA-256 hexdigest
   >>> user = User.get_user_by_act_code(code)
   >>> deadline = 172800 # this is the deadline in seconds (48 hours)
   >>> user.is_interaction_timely('a', deadline)
   True

This method requires two arguments:

* ``type``: must be 'a' (activation), 'd' (delete), or 'r' (reset password)
* ``deadline``: action deadline in seconds

It returns a boolean value of the success status.

If there are no actions to confirm (e.g., no pending activation or
confirmation), ``UserAccountError`` is raised.

Clearing user action data
-------------------------

Once user action was confirmed, it is no longer necessary to keep interaction
data in the database. To clear the data, you can use the ``clear_interaction``
instance method. ::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.clear_interaction()
   >>> user.store()

After clearing the interaction data, the account must be stored to make the
changes permanent.

Activating a user account
-------------------------

Calling ``activate`` on User instance activates it. Although it is activated
in-memory, it is not saved as activated, so you need to store changes before a
user can log in. ::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.activate()
   >>> user.store()

Creating a user with activation
-------------------------------

You can both create and activate a user account at the same time. To do that,
you can pass the ``activated`` argument to the ``create`` method::

   >>> from authenticationpy.auth import User
   >>> user = User(username='myuser',
   ...             email='user@someserver.com')
   >>> user.create(activated=True)
   >>> same_user = User.get_user(username='myuser')
   >>> same_user.active
   True

This results in a single database transaction, which is more efficient than
calling ``create`` without ``activated`` argument, and then calling
``activate`` explicitly.

Getting the user record
-----------------------

To get the user record, you can call the ``get_user`` class method::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(email="user@someserver.com")

You can use either the username or the e-mail address as an argument for the
``get_user`` method.

Authenticating the user
-----------------------

To check the user password (to authenticate it), you must call the
``authenticate`` method on the user object::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.authenticate('clear-text password')
   True

The method returns True or False depending on whether authentication was
successful. Note that due to web.py's session handling, authentication.py will
*not* automatically assign users to a session. It is your responsibility to do
so.

Password length constraints
---------------------------

Default password minimum length is 4 characters. If you want your users to use
a longer minimum length password (or shorter), you can customize the minimum
length by setting ``web.config.min_pwd_length`` configuration variable. In case
the password you are trying to set is shorter than minimum length,
``ValueError`` is raised. The same exception is raised when password is
0-length. Even if you set ``web.config.min_pwd_length`` to 0, 0-length
passwords are not allowed. The absolute minimum allowed password length is 1.

Resetting the password
----------------------

You can reset the user password in two ways. You can simply assign a new
clear-text password to the password property or you can call the
``reset_password`` method, which can optionally send out a notification or
confirmation e-mail.

Here is an example using the property method::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.password = 'new password'
   >>> user.store()

And the ``reset_password`` method::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.reset_password('new password', 
                           message='Your password is now $password')

The template for the ``message`` argument can contain the following variables:

* ``$url``: automatically generated confirmation url suffix
* ``$username``: username
* ``$email``: the user's e-mail address
* ``$password``: the new cleartext password

If you are not sending any confirmation e-mail, but you still use a different
confirmation method, you can use the ``confirmation`` argument. If you pass
this argument with the value of ``True``, ``reset_password`` will behave as if
you have passed it the ``message`` argument.

You can also set the new password even if you send the e-mail. For example, if
you want to simply notify your user, rather than request an action, you can
pass the ``confirmation`` argument with the value of ``False`` and omit the URL
from URL from your message.

Confirming password resets
--------------------------

If you have decided to delay confirmation and send out a confirmation e-mail
when using ``password_reset`` method, you need to explicitly assign the new
password. The pending password (password that is still not confirmed) is stored
in encrypted form in the database. In order to assign that password as the new
password, you can call ``confirm_password``.

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.confirm_password()
   >>> user.store()

As you can see, you have to call ``store`` to make the changes permanent.

Sending e-mails to a user
-------------------------

Arbitrary e-mail messages can be sent to users using the ``send_email`` method.
Here's a simple example::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.send_email(message="Hi!")

As will all other e-mail facilities, you can add template variables to the
e-mail message, but for regular e-mail, you can use any template variables you
like. Just pass any variable as a keyword argument::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.send_email(message="$greeting, $username",
   ...                 greeting="Hello,",
   ...                 username=user.username)

Deleting a user
---------------

To delete a user (i.e, permanently remove its records), you can use the
``delete`` class method::

   >>> from authenticationpy.auth import User
   >>> User.delete(username='myuser')

You can also send out a notification or confirmation e-mail::

   >>> from authenticationpy.auth import User
   >>> User.delete(username='myuser',
   ...             message="""
   ...             Please confirm this by clicking on this link:
   ...             http://mysite.com/confirm/$url
   ...             """)

If you decide to send a confirmation e-mail, the user account is not removed
until its removal is confirmed by ``confirm_delete`` class method.

Available e-mail message template variables are::

* ``$url``: automatically generated confirmation url suffix
* ``$username``: username
* ``$email``: the user's e-mail address

Confirming user account removal
-------------------------------

If you have used the confirmation e-mail functionality when deleting a user
account, you need to explcitly confirm account removal. You can do that by
using the ``confirm_delete`` class method::

   >>> from authenticationpy.auth import User
   >>> User.confirm_delete(username='myuser')

Suspending an account
---------------------

Users account can be deleted, and it's gone forever. If you only want to
disable authentication for a particular account, you can suspend it instead of
deleting it. To do this, just use the ``suspend`` class method. It is an e-mail
method, like some of the other methods. ::

   >>> from authenticationpy.auth import User
   >>> User.suspend(username='myuser',
   ...              message="""
   ...              Hi, $username,
   ...              Your account has been suspended, can you believe it?
   ...              """)

Unlike other e-mail methods, ``suspend`` doesn't require any confirmation, so
the only available template variables are:

* ``$username``: account username
* ``$email``: user's e-mail address

You can also suspend an account by simply deactivating it::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.active = False
   >>> user.store()

Trying to authenticate using a suspended account will result in a
``UserAccountError`` exception.

Updating user details
---------------------

Updating properties for a user is as simple as assigning new values to them.
Some validation occurs behind the scene (e.g, creating the encrypted version of
the password), but most of the time you don't need to worry about that. Once
you've assigned new properties for the user, you have to call the ``store``
method to write changes to the database. ::

   >>> from authenticationpy.auth import User
   >>> user = User.get_user(username='myuser')
   >>> user.username = 'mynewuser'
   >>> user.store()

You should note that even the e-mail address can be changed. It is your
responsibility to prevent that if you don't want your users to change the
e-mail address.
Something went wrong with that request. Please try again.