web2pyTM Tools

Since version 1.56 web2py includes tools for authentication, authorization, CRUD, web services, and more. They are implemented so that they do not require JOINs and work on Google App Engine as well.

Authentication

The basic authentication tool is the Auth class. It provides methods that can be used as controller actions to register users (with optional Recaptcha support), log them in and out, allow email verification, password change, password reset and retrieval, editing user profile.

These functionalities can then be used as the basis for authorization.

The Auth calls can be extended, personalized, and replaced by other authentication mechanisms which expose a similar interface.

To use authentication, write something like this in a model file:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
from gluon.tools import Mail, Auth, Recaptcha

mail = Mail()
## specify your SMTP server
mail.settings.server = 'smtp.yourdomain.com:25'
## specify your email address
mail.settings.sender = 'you@yourdomain.com'
## optional: specify the username and password for SMTP
mail.settings.login = 'username@password'

## instantiate the Auth class (or your derived class)
auth = Auth(globals(), db)
## ask it to create all necessary tables
auth.define_tables()
## optional: require email verification for registration
# auth.settings.mailer = mail
## optional: if you require captcha verification for registration
# auth.settings.captcha = Recaptcha(request,public_key='RECAPTCHA_PUBLIC_KEY',private_key='RECAPTCHA_PRIVATE_KEY')

In your controller (for example in default.py) expose the auth object (for example via a user action):

1.
2.
def user():
return dict(form = auth())

The above action will expose the following URLs

The groups page shows a list of roles and description for the groups the logged-in user is a member of (see authorization).

You can check if a user is logged in via auth.is_logged_in(). If a user is logged in its record information can be found in auth.user. By default a user is stored in a table called "auth_user" and has the following columns: first_name, last_name, email, password. Password is stored encrypted (md5 hashed or stronger). You can change the table name, define your own table with more fields, require additional validators.

All authentication events are logged in a table called "auth_event".

Custom Authentication

You can customize the auth object by changing its settings anf messages:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
auth.messages.access_denied = 'Insufficient privileges'
auth.messages.logged_in = 'Logged in'
auth.messages.email_sent = 'Email sent'
auth.messages.unable_to_send_email = 'Unable to send email'
auth.messages.email_verified = 'Email verified'
auth.messages.logged_out = 'Logged out'
auth.messages.registration_successful = 'Registration successful'
auth.messages.invalid_email = 'Invalid email'
auth.messages.invalid_login = 'Invalid login'
auth.messages.invalid_user = 'Invalid user'
auth.messages.mismatched_password = "Password fields don't match"
auth.messages.verify_email =
'Click on the link http://...verify_email/%(key)s to verify your email'
auth.messages.verify_email_subject = 'Password verify'
auth.messages.username_sent = 'Your username was emailed to you'
auth.messages.new_password_sent = 'A new password was emailed to you'
auth.messages.password_changed = 'Password changed'
auth.messages.retrieve_username = 'Your username is: %(username)s'
auth.messages.retrieve_username_subject = 'Username retrieve'
auth.messages.retrieve_password = 'Your password is: %(password)s'
auth.messages.retrieve_password_subject = 'Password retrieve'
auth.messages.profile_updated = 'Profile updated'
auth.messages.new_password = 'New password'
auth.messages.old_password = 'Old password'

Strings can be translated in the usual way even if there is not T operator

Views are handled the usual web2py way.

It also possible to use custom tables. For example a custom user table:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
# instantiate auth
auth=Auth(globals(),db)

auth.settings.table_user = db.define_table('auth_user',
Field('first_name', length=512,default=''),
Field('last_name', length=512,default=''),
Field('email', length=512,default='',
requires = [IS_EMAIL(),IS_NOT_IN_DB(db,'auth_user.email')]),
Field('password', 'password', readable=False,
label='Password',
requires=CRYPT(auth.settings.hmac_key)),
Field('registration_key', length=512,
writable=False, readable=False,default=''),
Field('reset_password_key', length=512,
writable=False, readable=False, default=''))

# define any other requires table
auth.define_tables()

One can add any other field that is needed as long as the fields in the example are there.

Authorization

Once you have a user identified by user.id, you can create a group (for example "Manager")


group_id = auth.add_group(role = "Manager", description = "example of a group")

make the user a member:


auth.add_membership(group_id,user_id)

and assign permissions to all members of the group:


auth.add_permission(group_id,'create','tablename',record_id)

Now you can enforce permissions using the following decorators:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
auth.settings.on_failed_authorization=URL(r=request,f='error')

@auth.requires_login()
def some_function1():
return dict()

@auth.requires_membership('Manager')
def some_function2():
return dict()

@auth.requires_permission('create','tablename',1)
def some_function3():
return dict()

def error():
return dict(message = T("not authorized"))

Group roles are conventional. Permission names are also conventional. If one group has a certain permission on record_id==0 (default), it means the user has the permission on any record.

Some permission names ("tables", "select", "create", "update", "delete", "read") have a special meaning because they can be automatically enforced by the CRUD tool described below.

Create/Read/Update/Delete and more

To use CRUD, in your model insert the following code:

1.
2.
from gluon.tools import Crud
crud = Crud(globals(),db)

and in a controller (for example in default.py) expose it via an action (for example data):

1.
2.
3.
4.
5.
def data():
return dict(form = crud())

def download():
return response.download(request, db)

This will expose the following URLs:

"tables" list of current database tables.

"download" only allows to download uploaded files (whether they are in the uploads folder or in the database).

To enforce authorization on these CRUD URLs simply set

1.
crud.settings.auth=auth

and the logged in user will only be able to "create" a record in table "tablename" if the user is a member of a group that has "create" permission on table "tablename". The same mechanism works for tables, select, read, update, and delete URLs.

Services

Web2py provides an interface to expose any function as a service (CSV, XML, JSON, XMLRPC, JSONRPC, AMF)

This is achieved in three steps:

The function can now be called remotely using the syntax

1.
http://..../app/default/call/[method]/[function]/[arguments]
or
1.
http://..../app/default/call/[method]/[function]?[arguments]
for example
1.
2.
http://..../app/default/call/run/myfunction/2/3
http://..../app/default/call/json/myfunction/2/3
or
1.
2.
http://..../app/default/call/run/myfunction?a=2&b=3
http://..../app/default/call/json/myfunction?a=2&b=3

Caveats: csv only works if the function returns a list of list. amfrpc only works if PyAMF is installed. In the case of xmlrpc, jsonrpc and amfrpc, the arguments (/myfunction/2/3) cannot be passed in the URI, they must be passed according to the respective protocol. For example for xmlrpc from another python program:

1.
2.
3.
>>> from xmlrpclib import ServerProxy
>>> server = ServerProxy('http://..../app/default/call/xmlrpc')
>>>
print server.myfunction(2,3)

Fetch a URL

The Python module urllib does not work well on the Google App Engine. For this reason we created a portable function for fetching url that works everywhere, including GAE:

1.
2.
from gluon.tools import fetch
html = fetch("http://www.web2py.com")

Geocoding

Another very common need of modern web applications is that of converting an address into latitude and longitude. We also provide a portable function to do it that uses the Google Geocoding API:

1.
2.
from gluon.tools import geocode
(latitude, longitude) = geocode("243 S Wabash Ave, Chicago IL 60604, USA")

It returns 0,0 on failure.