Cork - Authentication for the Bottle web framework
Cork provides a simple set of methods to implement Authentication and Authorization in web applications based on Bottle.
It is designed to stay out of the way and let you focus on what your application should do.
News:
- 2013-01-27: Version 0.6
- More flexible file naming in JsonBackend.
- Fixed function to update user’s email address.
- More informative log message for missing Pycrypto.
- 2012-12-04: Version 0.5
- SMTP SSL support added, smtp_url parsing improved.
- requirements.txt added.
- 2012-11-25: Version 0.4
- Check added to prevent multiple registrations of the same account.
- 2012-11-18: Version 0.3
- PBKDF2 hash length check added
- Multi-platform unit testing
- 2012-10-04: Version 0.2
- SMTP URL added: support for STARTTLS, SSL and configurable port numbers
- Bugfix: login() redirects to fail_redirect if an username is not provided
- Better password hashing, multiple hash formats supported
- 2012-07-09: Version 0.1 - Improved installation
- 2012-06-10: Version 0.1~beta3 - Improved registration and password reset
Cork is under development - contributions are welcome.
Features
- Minimal design, easy to tweak.
- Designed for web application with moderate userbases. User credentials are stored in JSON files.
- Simple role-based authentication. User are authorized by role e.g. ‘admin’, ‘user’, ‘editor’.
- Admin users can create and delete user accounts and roles.
- User registration and password reset using email delivery and confirmation.
- Unit-tested and almost fully code covered
- Multiple backends support (e.g. storing users/roles in a key/value database).
- Thread safe.
Roadmap
- Additional hooks to provide logging or user-defined functions in case of login()/require() failure
- Hooks to share session data between multiple hosts
- Flask support
Basic usage
Installation:
$ pip install bottle-cork
or
$ easy_install bottle-cork
Use virtualenv on package-based Linux distributions! Learn why
A fully working example is provided with the Cork sources
Example web application:
from cork import Cork
# Use users.json and roles.json in the local example_conf directory
aaa = Cork('example_conf')
@bottle.route('/login', method='POST')
def login():
username = request.POST.get('user', '')
password = request.POST.get('pwd', '')
aaa.login(username, password, success_redirect='/', fail_redirect='/login')
@bottle.route('/logout')
def logout():
aaa.current_user.logout(redirect='/login')
@bottle.route('/')
def index():
"""Only authenticated users can see this"""
aaa.require(fail_redirect='/sorry_page')
return "Welcome %s" % aaa.current_user.username
@bottle.route('/admin')
def admin():
"""Only administrators can see this"""
aaa.require(role='admin', fail_redirect='/sorry_page')
return 'Welcome administrators'
@bottle.route('/register', method='POST')
def register():
"""Users can create new accounts, but only with 'user' role"""
username = request.POST.get('user', '')
password = request.POST.get('pwd', '')
email_addr = request.POST.get('email_addr', '')
aaa.register(username, password, email_addr)
return 'Please check your inbox.'
# Web application main
def main():
session_opts = {
'session.type': 'cookie',
'session.validate_key': True,
}
# Setup Beaker middleware to handle sessions and cookies
app = bottle.default_app()
app = SessionMiddleware(app, session_opts)
# Start the Bottle webapp
bottle.run(app=app, reloader=True)
if __name__ == "__main__":
main()
Code documentation
-
exception cork.cork.AAAException[source]
Bases: exceptions.Exception
Generic Authentication/Authorization Exception
-
args
-
message
-
exception cork.cork.AuthException[source]
Bases: cork.cork.AAAException
Authentication Exception: incorrect username/password pair
-
args
-
message
-
class cork.cork.JsonBackend(directory, users_fname='users', roles_fname='roles', pending_reg_fname='register', initialize=False)[source]
Bases: object
Data storage class. Handles JSON files
| Parameters: |
- users_fname (str.) – users file name (without .json)
- roles_fname (str.) – roles file name (without .json)
- pending_reg_fname (str.) – pending registrations file name (without .json)
- initialize (bool.) – create empty JSON files (defaults to False)
|
-
save_users()
Save users in a JSON file
-
save_roles()
Save roles in a JSON file
-
save_pending_registrations()
Save pending registrations in a JSON file
-
class cork.cork.Cork(directory, email_sender=None, users_fname='users', roles_fname='roles', pending_reg_fname='register', initialize=False, session_domain=None, smtp_url='localhost', smtp_server=None)[source]
Bases: object
Auth/Authorization/Accounting class
| Parameters: |
- directory (str.) – configuration directory
- users_fname (str.) – users filename (without .json), defaults to ‘users’
- roles_fname (str.) – roles filename (without .json), defaults to ‘roles’
|
-
login(username, password, success_redirect=None, fail_redirect=None)[source]
Check login credentials for an existing user.
Optionally redirect the user to another page (tipically /login)
| Parameters: |
- username (str.) – username
- password (str.) – cleartext password
- success_redirect (str.) – redirect authorized users (optional)
- fail_redirect (str.) – redirect unauthorized users (optional)
|
| Returns: | True for successful logins, else False
|
-
logout(success_redirect='/login', fail_redirect='/login')[source]
Log the user out, remove cookie
| Parameters: |
- success_redirect (str.) – redirect the user after logging out
- fail_redirect (str.) – redirect the user if it is not logged in
|
-
require(username=None, role=None, fixed_role=False, fail_redirect=None)[source]
Ensure the user is logged in has the required role (or higher).
Optionally redirect the user to another page (tipically /login)
If both username and role are specified, both conditions need to be
satisfied.
If none is specified, any authenticated user will be authorized.
By default, any role with higher level than role will be authorized;
set fixed_role=True to prevent this.
| Parameters: |
- username (str.) – username (optional)
- role (str.) – role
- fixed_role (bool.) – require user role to match role strictly
- redirect (str.) – redirect unauthorized users (optional)
|
-
create_role(role, level)[source]
Create a new role.
| Parameters: |
- role (str.) – role name
- level (int.) – role level (0=lowest, 100=admin)
|
| Raises : | AuthException on errors
|
-
delete_role(role)[source]
Deleta a role.
| Parameters: | role (str.) – role name |
| Raises : | AuthException on errors |
-
list_roles()[source]
List roles.
| Returns: | (role, role_level) generator (sorted by role) |
-
create_user(username, role, password, email_addr=None, description=None)[source]
Create a new user account.
This method is available to users with level>=100
| Parameters: |
- username (str.) – username
- role (str.) – role
- password (str.) – cleartext password
- email_addr (str.) – email address (optional)
- description (str.) – description (free form)
|
| Raises : | AuthException on errors
|
-
delete_user(username)[source]
Delete a user account.
This method is available to users with level>=100
| Parameters: | username (str.) – username |
| Raises : | Exceptions on errors |
-
list_users()[source]
List users.
| Returns: | (username, role, email_addr, description) generator (sorted by |
username)
-
current_user[source]
Current autenticated user
| Returns: | User() instance, if authenticated |
| Raises : | AuthException otherwise |
-
user(username)[source]
Existing user
| Returns: | User() instance if the user exist, None otherwise |
-
register(username, password, email_addr, role='user', max_level=50, subject='Signup confirmation', email_template='views/registration_email.tpl', description=None)[source]
Register a new user account. An email with a registration validation
is sent to the user.
WARNING: this method is available to unauthenticated users
| Parameters: |
- username (str.) – username
- password (str.) – cleartext password
- role (str.) – role (optional), defaults to ‘user’
- max_level (int.) – maximum role level (optional), defaults to 50
- email_addr (str.) – email address
- subject (str.) – email subject
- email_template (str.) – email template filename
- description (str.) – description (free form)
|
| Raises : | AssertError or AAAException on errors
|
-
validate_registration(registration_code)[source]
Validate pending account registration, create a new account if
successful.
| Parameters: | registration_code (str.) – registration code |
-
send_password_reset_email(username=None, email_addr=None, subject='Password reset confirmation', email_template='views/password_reset_email')[source]
Email the user with a link to reset his/her password
If only one parameter is passed, fetch the other from the users
database. If both are passed they will be matched against the users
database as a security check
| Parameters: |
- username (str.) – username
- email_addr (str.) – email address
- subject (str.) – email subject
- email_template (str.) – email template filename
|
| Raises : | AAAException on missing username or email_addr,
AuthException on incorrect username/email_addr pair
|
-
reset_password(reset_code, password)[source]
Validate reset_code and update the account password
The username is extracted from the reset_code token
| Parameters: |
- reset_code (str.) – reset token
- password (str.) – new password
|
| Raises : | AuthException for invalid reset tokens, AAAException
|
-
class cork.cork.User(username, cork_obj, session=None)[source]
Bases: object
Represent an authenticated user, exposing useful attributes:
username, role, level, session_creation_time, session_accessed_time,
session_id. The session-related attributes are available for the
current user only.
| Parameters: |
- username (str.) – username
- cork_obj – instance of Cork
|
-
update(role=None, pwd=None, email_addr=None)[source]
Update an user account data
| Parameters: |
- role (str.) – change user role, if specified
- pwd (str.) – change user password, if specified
- email_addr (str.) – change user email address, if specified
|
| Raises : | AAAException on nonexistent user or role.
|
-
delete()[source]
Delete user account
| Raises : | AAAException on nonexistent user. |
-
class cork.cork.Mailer(sender, smtp_url, join_timeout=5)[source]
Bases: object
Send emails asyncronously
| Parameters: |
- sender (str.) – Sender email address
- smtp_server (str.) – SMTP server
|
-
send_email(email_addr, subject, email_text)[source]
Send an email
| Parameters: |
- email_addr (str.) – email address
- subject (str.) – subject
- email_text (str.) – email text
|
| Raises : | AAAException if smtp_server and/or sender are not set
|
-
join()[source]
Flush email queue by waiting the completion of the existing threads