This is an authentication module for web.py, code originally by jpscaletti.
The module allows you to:
-
Limit access to pages based on if the user is logged in, if is authorized (by checking against a table of permissions) or meet certain conditions.
-
Generate and manage password hashes. Sha1, sha512 and bcrypt are supported.
-
Authenticate a user by checking a login and password against a database of users.
-
Generate and validate tokens for passwords resets.
-
CAPTCHA validation(Only visual CAPTCHAs) (New)
It also includes default pages to login, generate a password-reset token, email the token and set a new password after the token is validated.
-
Put the auth package in the
web/contrib/
folder. -
Create the following tables:
CREATE TABLE user ( user_id int NOT NULL AUTO_INCREMENT PRIMARY KEY, user_login varchar(64) NOT NULL, user_password varchar(255) NOT NULL, user_email varchar(64), # Optional, see settings user_status varchar(16) NOT NULL DEFAULT 'active', user_last_login datetime NOT NULL ) CREATE TABLE permission ( permission_id int NOT NULL AUTO_INCREMENT PRIMARY KEY, permission_codename varchar(50), # Example: 'can_vote' permission_desc varchar(50) # Example: 'Can vote in elections' ) CREATE TABLE user_permission ( up_user_id int REFERENCES user (user_id), up_permission_id int REFERENCES permission (permission_id), PRIMARY KEY (up_user_id, up_permission_id) )
-
Generally use in your code:
import web from web.contrib.auth import auth urls = ( '/', 'index', ) app = web.application(urls, locals()) db = web.database(dbn='mysql', db='webpy', user='scott', pw='tiger') settings = {} auth.init_app(app, db, **settings)
The system will create and use a DiskStore session. If you want to use an existing one or another type of session, you pass it as an argument.
mysession = web.session.Session(app, web.session.DiskStore('sessions')) auth.init_app(app, db, mysession, **settings)
-
Enable CAPTCHA validation
To enable the CAPTCHA validation, you need an extra CAPTCHA module which could generate the CAPTCHAs(visual CAPTCHAs) and the text-based check-code(can be input by user on the authentication page). You should use a wrapper function, which has No parameters and exact TWO return values. One is the CAPTCHA image, an
str
instance, the value or content of acStringIO.StringO
orStringIO.StringO
object(see here for details), whose file type determines the value ofcaptcha_image_type
. The other one is the CAPTCHA check-code string. Then, pass the wrapper function and the image type as arguments.from Captcha import captcha_func settings = dict({ 'captcha_func': captcha_func, 'captcha_image_type': 'png', }) auth.init_app(app, db, **settings)
Once you initiated the auth application a number of methods are available:
- @auth.protected(**pars)
-
Decorator for limiting the access to pages.
Examples:
Limiting access to authenticated users
class SomePage: @auth.protected() def GET(self): ...
Limiting access to users with a specific permission
class SomePage: @auth.protected(perm='can_edit') def GET(self): ...
Limiting access to users who pass a test
def over18(user): return user.age > 18 class SomePage: @auth.protected(test=over18) def GET(self): ...
Limiting access to users who need to pass the CAPTCHA validation
class SomePage: @auth.protected(captcha_on=True) def GET(self): ...
If the CAPTCHA validation is failed or the user isn't authorized it'll be redirected to
settings.url_login
('/login' by default). - auth.authenticate(login, password)
-
Validates the user's credentials. If they are valid returns a user object (minus the password hash) or
None
if not.user = auth.authenticate(login='john', password='secret') if not user: return 'That's correct' else: return 'Wrong!'
This function does not log in the user. Use
auth.login()
for that. - auth.login(user)
- Set the user as logged in.
- auth.check_password(password, stored_passw)
- Returns a boolean of whether the password was correct.
- auth.logout()
- Flush the authenticated user session
- auth.user_exist(login)
- Return
True
if a user with that login already exist. - auth.create_user(login, password=None, perms=[], **data)
-
Create a new user and returns the
user_id
.
If password is None, it will marks the user as having no password (
check_password()
for this user will never returnTrue
). - auth.set_password(login, password=None)
- Sets the password of an already existing user to the given raw string, taking care of the password hashing.
- auth.update_user(login, **data)
- Update the user's data taking care of the password hashing if one is provided.
- auth.get_user(login=None)
-
Returns a user object (minus the password hash).
If login is
None
returns the currently authenticated user object orNone
if there isn't one - auth.pass_test(user=None)
-
Return True if the user pass the test.
test must be a function that takes a user object and returns
True
orFalse
. - auth.has_perm(perm, user=None)
-
Return True if the user has the permission.
Perm can be either a single permission (string) or a sequence of them.
- auth.get_permissions(user=None)
- Returns a list of permission strings that the user has.
- auth.create_permission(codename, desc)
-
Creates a new permission. If the permission already exists it update the description.
Example:
create_permission('new_posts', 'Can write new posts')
- auth.delete_permission(codename)
- Deletes a permission
- auth.add_permission(perm, user_id)
- Assign an existing permission to a user.
- auth.remove_permission(perm, user_id)
- Removes the permission.
- auth.login_form(auth)
- A login form to be used inside other pages (like the home page).
By default the system will map a login, logout and password-reset pages. This can be disabled in the settings.
- hash_type = 'sha512'
- Either sha1, sha512 (default) or bcrypt — the algorithm used to perform a one-way hash of the password. Note that bcrypt is only supported on platforms that have the Python py-bcrypt module available.
- hash_depth = 12
- How many time the password is recursively hashed.
- db_email_field = 'user_email'
- Field of the user table that contains the email address. For instance, if you want to use the email as the username, you can change this to 'user_login'.
- password_minlen = 6
-
Minimum password length.
set_password()
andcreate_user
will raise an "AuthError, 'bad password'" if the password is shorter than this. - forced_delay = 0.5
- Artificial delay (in seconds) to slow down brute-force attacks
- auto_map = True
-
Disable the auto-magically generated pages for login, logout and password reset. If it is
True
, it will use the url_* and template_* settings. - captcha_enabled = False
- CAPTCHA validation is disabled by default. You could enable it automatically by define an extra CAPTCHA generation module as described above.
- captcha_image_type = 'png'
-
The default file type of the CAPTCHA image, which is decided by the module which generates the CAPTCHA image. You could set the value here without passing a dict argument
settings
that contains it. - url_captcha = '/captcha'
- The URL for the CAPTCHA image.
- url_login = '/login'
- The URL for the log in page.
- url_logout = '/logout'
- The URL for the log out.
- url_after_login = '/'
- Go there after a successful login
- template_login = None
-
A template function:
render = web.template.render('/templates') settings = dict( template_login = render.mylogin, ) auth = DBAuth(app, db, **settings)
If
None
, the default template will be used. Seeweb/contrib/auth/templates/login.html
- url_reset_token = '/password_reset'
- The URL of the password-reset token generation page.
- url_reset_change = '/password_reset'
- The URL for the password change page.
- template_reset_token = None
-
A template function. If None, the default template will be used. See
web/contrib/auth/templates/reset_token.html
- template_reset_email = None
-
A template function. If None, the default template will be used. See
web/contrib/auth/templates/reset_email.html
- template_reset_change = None
-
A template function. If None, the default template will be used. See
web/contrib/auth/templates/reset_change.html
- reset_expire_after = 2
- Number of hours than the password-reset token will be valid.
- email_from = ''
- The password-reset email “From:” header.
This module is released under the MIT license (MIT), anyone is welcome to contribute.