Source code for flaskext.auth.auth
"""
Base module of the extension. Contains basic functions, the Auth object and the
AuthUser base class.
"""
import time, hashlib, datetime
from functools import partial
from flask import session, abort, current_app, redirect, url_for
DEFAULT_HASH_ALGORITHM = hashlib.sha1
DEFAULT_USER_TIMEOUT = 3600
SESSION_USER_KEY = 'auth_user'
SESSION_LOGIN_KEY = 'auth_login'
def _default_not_authorized(*args, **kwargs):
return abort(401)
def _redirect_to_login(login_url_name):
return redirect(url_for(login_url_name))
[docs]class Auth(object):
"""
Extension initialization object containing settings for the extension.
Supported settings:
- login_url_name: Name of the URL that is used for login. It's used in
the not_logged_in_callback if provided in the constructor.
- not_logged_in_callback: Function to call when a user accesses a page
without being logged in. Normally used to redirect to the login page.
If a login_url_name is provided, it will by default redirect to that
url. Otherwise, the default is abort(401).
- not_permitted_callback: Function to call when a user tries to access
a page for which he doesn't have the permission. Default: abort(401).
- hash_algorithm: Algorithm from the hashlib library used for password
encryption. Default: sha1.
- user_timeout: Timeout (in seconds) after which the sesion of the user
expires. Default: 3600. A timeout of 0 means it will never expire.
- load_role: Function to load a role. Is called with user.role as only
parameter.
"""
def __init__(self, app=None, login_url_name=None):
if login_url_name is None:
self.not_logged_in_callback = _default_not_authorized
else:
self.not_logged_in_callback = partial(_redirect_to_login,
login_url_name)
self.not_permitted_callback = _default_not_authorized
self.hash_algorithm = DEFAULT_HASH_ALGORITHM
self.user_timeout = DEFAULT_USER_TIMEOUT
self.load_role = lambda _: None
if app is not None:
self.init_app(app)
def init_app(self, app):
app.auth = self
[docs]class AuthUser(object):
"""
Baseclass for a user model. Contains a few convenience methods.
Attributes:
- username: Username of the user.
- password: Password of the user. By default not encrypted. The
set_and_encrypt_password() method sets and encrypts the password.
- salt: Salt used for the encrytion of the password.
- role: Role of this user. """
role = None
def __init__(self, username=None, password=None, salt=None, role=None):
self.username = username
# Storing password unmodified. Encryption of the password should
# happen explicitly.
self.password = password
self.salt = salt
self.role = role
[docs] def set_and_encrypt_password(self, password, salt=str(int(time.time()))):
"""
Encrypts and sets the password. If no salt is provided, a new
one is generated.
"""
self.salt = salt
self.password = encrypt(password, self.salt)
[docs] def authenticate(self, password):
"""
Attempts to verify the password and log the user in. Returns true if
succesful.
"""
if self.password == encrypt(password, self.salt):
login(self)
return True
return False
def __eq__(self, other):
return self.username == getattr(other, 'username', None)
def __ne__(self, other):
return not self.__eq__(other)
def __getstate__(self):
return self.__dict__
@classmethod
[docs] def load_current_user(cls, apply_timeout=True):
"""
Load current user based on the result of get_current_user_data().
"""
data = get_current_user_data(apply_timeout)
if not data:
return None
user = cls()
user.__dict__ = data
return user
def is_logged_in(self):
user_data = get_current_user_data()
return user_data is not None and user_data.get('username') == self.username
[docs]def encrypt(password, salt=None, hash_algorithm=None):
"""Encrypts a password based on the hashing algorithm."""
to_encrypt = password
if salt is not None:
to_encrypt += salt
if hash_algorithm is not None:
return hash_algorithm(to_encrypt).hexdigest()
return current_app.auth.hash_algorithm(to_encrypt).hexdigest()
[docs]def login(user):
"""
Logs the user in. Note that NO AUTHENTICATION is done by this function. If
you want to authenticate a user, use the AuthUser.authenticate() method.
"""
session[SESSION_USER_KEY] = user.__getstate__()
session[SESSION_LOGIN_KEY] = datetime.datetime.utcnow()
[docs]def logout():
"""Logs the currently logged in user out and returns the user data."""
session.pop(SESSION_LOGIN_KEY, None)
return session.pop(SESSION_USER_KEY, None)
[docs]def get_current_user_data(apply_timeout=True):
"""
Returns the data of the current user (user.__dict__) if there is a
current user and he didn't time out yet. If timeout should be ignored,
provide apply_timeout=False.
"""
user_data = session.get(SESSION_USER_KEY, None)
if user_data is None:
return None
if not apply_timeout:
return user_data
login_datetime = session[SESSION_LOGIN_KEY]
now = datetime.datetime.utcnow()
user_timeout = current_app.auth.user_timeout
if user_timeout > 0 and now - login_datetime > \
datetime.timedelta(seconds=user_timeout):
logout()
return None
return user_data
[docs]def not_logged_in(callback, *args, **kwargs):
"""
Executes not logged in callback. Not for external use.
"""
if callback is None:
return current_app.auth.not_logged_in_callback(*args, **kwargs)
else:
return callback(*args, **kwargs)
[docs]def login_required(callback=None):
"""
Decorator for views that require login. Callback can be specified to
override the default callback on the auth object.
"""
def wrap(func):
def decorator(*args, **kwargs):
if get_current_user_data() is None:
return not_logged_in(callback, *args, **kwargs)
return func(*args, **kwargs)
return decorator
return wrap