mirror of
https://github.com/wassname/flask-security.git
synced 2026-06-27 16:10:11 +08:00
Added a bunch of code documentation
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
[submodule "docs/_themes"]
|
||||
path = docs/_themes
|
||||
url = git://github.com/mitsuhiko/flask-sphinx-themes.git
|
||||
Submodule
+1
Submodule docs/_themes added at 0269f3d188
+98
-30
@@ -1,15 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ext.security
|
||||
~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Flask-Security is a Flask extension module that aims to add quick and
|
||||
simple security via Flask-Login and Flask-Principal.
|
||||
Flask-Security is a Flask extension that aims to add quick and simple
|
||||
security via Flask-Login, Flask-Principal, Flask-WTF, and passlib.
|
||||
|
||||
:copyright: (c) 2012 by Matt Wright.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
|
||||
@@ -34,8 +33,11 @@ from passlib.context import CryptContext
|
||||
from werkzeug.utils import import_string
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
#: User model
|
||||
User = None
|
||||
|
||||
User, Role = None, None
|
||||
#: Role model
|
||||
Role = None
|
||||
|
||||
URL_PREFIX_KEY = 'SECURITY_URL_PREFIX'
|
||||
AUTH_PROVIDER_KEY = 'SECURITY_AUTH_PROVIDER'
|
||||
@@ -54,6 +56,7 @@ DEBUG_LOGOUT = 'User logged out, redirecting to: %s'
|
||||
FLASH_INACTIVE = 'Inactive user'
|
||||
FLASH_PERMISSIONS = 'You do not have permission to view this resource.'
|
||||
|
||||
#: Default Flask-Security configuration
|
||||
default_config = {
|
||||
URL_PREFIX_KEY: None,
|
||||
PASSWORD_HASH_KEY: 'plaintext',
|
||||
@@ -94,15 +97,15 @@ class UserIdNotFoundError(Exception):
|
||||
"""
|
||||
|
||||
class UserDatastoreError(Exception):
|
||||
"""Raise when a user datastore experiences an unexpected error
|
||||
"""Raised when a user datastore experiences an unexpected error
|
||||
"""
|
||||
|
||||
class UserCreationError(Exception):
|
||||
"""Raise when an error occurs when creating a user
|
||||
"""Raised when an error occurs when creating a user
|
||||
"""
|
||||
|
||||
class RoleCreationError(Exception):
|
||||
"""Raise when an error occurs when creating a role
|
||||
"""Raised when an error occurs when creating a role
|
||||
"""
|
||||
|
||||
|
||||
@@ -118,11 +121,24 @@ login_manager = LocalProxy(lambda: current_app.login_manager)
|
||||
#: Password encyption context
|
||||
pwd_context = LocalProxy(lambda: current_app.pwd_context)
|
||||
|
||||
# User service
|
||||
#: User datastore
|
||||
user_datastore = LocalProxy(lambda: getattr(current_app,
|
||||
current_app.config[USER_DATASTORE_KEY]))
|
||||
|
||||
def roles_required(*args):
|
||||
"""View decorator which specifies that a user must have all the specified
|
||||
roles. Example::
|
||||
|
||||
@app.route('/dashboard')
|
||||
@roles_required('admin', 'editor')
|
||||
def dashboard():
|
||||
return 'Dashboard'
|
||||
|
||||
The current user must have both the `admin` role and `editor` role in order
|
||||
to view the page.
|
||||
|
||||
:param args: The required roles.
|
||||
"""
|
||||
roles = args
|
||||
perm = Permission(*[RoleNeed(role) for role in roles])
|
||||
def wrapper(fn):
|
||||
@@ -144,6 +160,19 @@ def roles_required(*args):
|
||||
|
||||
|
||||
def roles_accepted(*args):
|
||||
"""View decorator which specifies that a user must have at least one of the
|
||||
specified roles. Example::
|
||||
|
||||
@app.route('/create_post')
|
||||
@roles_accepted('editor', 'author')
|
||||
def create_post():
|
||||
return 'Create Post'
|
||||
|
||||
The current user must have either the `editor` role or `author` role in
|
||||
order to view the page.
|
||||
|
||||
:param args: The possible roles.
|
||||
"""
|
||||
roles = args
|
||||
perms = [Permission(RoleNeed(role)) for role in roles]
|
||||
def wrapper(fn):
|
||||
@@ -166,6 +195,7 @@ def roles_accepted(*args):
|
||||
|
||||
|
||||
class RoleMixin(object):
|
||||
"""Mixin for `Role` model definitions"""
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name
|
||||
|
||||
@@ -177,10 +207,16 @@ class RoleMixin(object):
|
||||
|
||||
|
||||
class UserMixin(BaseUserMixin):
|
||||
"""Mixin for `User` model definitions"""
|
||||
|
||||
def is_active(self):
|
||||
"""Returns `True` if the user is active."""
|
||||
return self.active
|
||||
|
||||
def has_role(self, role):
|
||||
"""Returns `True` if the user identifies with the specified role.
|
||||
|
||||
:param role: A role name or `Role` instance"""
|
||||
if not isinstance(role, Role):
|
||||
role = Role(name=role)
|
||||
return role in self.roles
|
||||
@@ -196,18 +232,25 @@ class AnonymousUser(AnonymousUserBase):
|
||||
self.roles = [] # TODO: Make this immutable?
|
||||
|
||||
def has_role(self, *args):
|
||||
"""Returns `False`"""
|
||||
return False
|
||||
|
||||
|
||||
class Security(object):
|
||||
"""The :class:`Security` class initializes the Flask-Security extension.
|
||||
|
||||
:param app: The application.
|
||||
:param datastore: An instance of a user datastore.
|
||||
"""
|
||||
def __init__(self, app=None, datastore=None):
|
||||
self.init_app(app, datastore)
|
||||
|
||||
def init_app(self, app, datastore):
|
||||
"""Initialize the application
|
||||
|
||||
:param app: An instance of an application
|
||||
:param datastore: An instance of a datastore for your users
|
||||
"""Initializes the Flask-Security extension for the specified
|
||||
application and datastore implentation.
|
||||
|
||||
:param app: The application.
|
||||
:param datastore: An instance of a user datastore.
|
||||
"""
|
||||
if app is None or datastore is None: return
|
||||
|
||||
@@ -220,9 +263,6 @@ class Security(object):
|
||||
|
||||
app.config.update(configured)
|
||||
config = app.config
|
||||
#config = default_config.copy()
|
||||
#config.update(app.config.get(AUTH_CONFIG_KEY, {}))
|
||||
#app.config[AUTH_CONFIG_KEY] = config
|
||||
|
||||
# setup the login manager extension
|
||||
login_manager = LoginManager()
|
||||
@@ -301,7 +341,7 @@ class Security(object):
|
||||
|
||||
|
||||
class LoginForm(Form):
|
||||
"""Default login form"""
|
||||
"""The default login form"""
|
||||
|
||||
username = TextField("Username or Email",
|
||||
validators=[Required(message="Username not provided")])
|
||||
@@ -317,16 +357,27 @@ class LoginForm(Form):
|
||||
|
||||
|
||||
class AuthenticationProvider(object):
|
||||
"""Default authentication provider"""
|
||||
"""The default authentication provider implementation.
|
||||
|
||||
:param login_form_class: The login form class to use when authenticating a
|
||||
user
|
||||
"""
|
||||
|
||||
def __init__(self, login_form_class=None):
|
||||
self.login_form_class = login_form_class or LoginForm
|
||||
|
||||
def login_form(self, formdata=None):
|
||||
"""Returns an instance of the login form with the provided form.
|
||||
|
||||
:param formdata: The incoming form data"""
|
||||
return self.login_form_class(formdata)
|
||||
|
||||
def authenticate(self, form):
|
||||
# first some basic validation
|
||||
"""Processes an authentication request and returns a user instance if
|
||||
authentication is successful.
|
||||
|
||||
:param form: An instance of a populated login form
|
||||
"""
|
||||
if not form.validate():
|
||||
if form.username.errors:
|
||||
raise BadCredentialsError(form.username.errors[0])
|
||||
@@ -336,6 +387,13 @@ class AuthenticationProvider(object):
|
||||
return self.do_authenticate(form.username.data, form.password.data)
|
||||
|
||||
def do_authenticate(self, user_identifier, password):
|
||||
"""Returns the authenticated user if authentication is successfull. If
|
||||
authentication fails an appropriate error is raised
|
||||
|
||||
:param user_identifier: The user's identifier, either an email address
|
||||
or username
|
||||
:param password: The user's unencrypted password
|
||||
"""
|
||||
try:
|
||||
user = user_datastore.find_user(user_identifier)
|
||||
except AttributeError, e:
|
||||
@@ -355,11 +413,15 @@ class AuthenticationProvider(object):
|
||||
raise BadCredentialsError("Password does not match")
|
||||
|
||||
def auth_error(self, msg):
|
||||
"""Sends an error log message and raises an authentication error.
|
||||
|
||||
:param msg: An authentication error message"""
|
||||
logger.error(msg)
|
||||
raise AuthenticationError(msg)
|
||||
|
||||
|
||||
def get_class_by_name(clazz):
|
||||
"""Get a reference to a class by its string representation."""
|
||||
parts = clazz.split('.')
|
||||
module = ".".join(parts[:-1])
|
||||
m = __import__( module )
|
||||
@@ -368,6 +430,7 @@ def get_class_by_name(clazz):
|
||||
return m
|
||||
|
||||
def get_class_from_config(key, config):
|
||||
"""Get a reference to a class by its configuration key name."""
|
||||
try:
|
||||
return get_class_by_name(config[key])
|
||||
except Exception, e:
|
||||
@@ -375,22 +438,27 @@ def get_class_from_config(key, config):
|
||||
"Could not get class '%s' for Auth setting '%s' >> %s" %
|
||||
(config[key], key, e))
|
||||
|
||||
def get_url(value):
|
||||
# try building the url or assume its a url already
|
||||
try: return url_for(value)
|
||||
except: return value
|
||||
def get_url(endpoint_or_url):
|
||||
"""Returns a URL if a valid endpoint is found. Otherwise, returns the
|
||||
provided value."""
|
||||
try:
|
||||
return url_for(endpoint_or_url)
|
||||
except:
|
||||
return endpoint_or_url
|
||||
|
||||
def get_post_login_redirect():
|
||||
"""Returns the URL to redirect to after a user logs in successfully"""
|
||||
return (get_url(request.args.get('next')) or
|
||||
get_url(request.form.get('next')) or
|
||||
find_redirect(POST_LOGIN_KEY))
|
||||
|
||||
def find_redirect(key):
|
||||
# Look in the session first, and if not there go to the config, and
|
||||
# if its not there either just go to the root url
|
||||
result = (get_url(session.get(key.lower(), None)) or
|
||||
get_url(current_app.config[key] or None) or '/')
|
||||
# Try and delete the session value if it was used
|
||||
try: del session[key.lower()]
|
||||
except: pass
|
||||
"""Returns the URL to redirect to after a user logs in successfully"""
|
||||
result = (get_url(session.pop(key.lower(), None)) or
|
||||
get_url(current_app.config[key.upper()] or None) or '/')
|
||||
|
||||
try:
|
||||
del session[key.lower()]
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
@@ -1,10 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ext.security.datastore
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains an abstracted user datastore.
|
||||
|
||||
:copyright: (c) 2012 by Matt Wright.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from flask.ext import security
|
||||
from flask.ext.security import UserCreationError, RoleCreationError, pwd_context
|
||||
|
||||
class UserDatastore(object):
|
||||
"""Abstracted user datastore. Always extend this and implement
|
||||
missing methods"""
|
||||
"""Abstracted user datastore. Always extend this class and implement the
|
||||
:attr:`get_models`, :attr:`_save_model`, :attr:`_do_with_id`,
|
||||
:attr:`_do_find_user`, and :attr:`_do_find_role` methods.
|
||||
|
||||
:param db: An instance of a configured databse manager from a Flask
|
||||
extension such as Flask-SQLAlchemy or Flask-MongoEngine"""
|
||||
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
def get_models(self):
|
||||
"""Returns configured `User` and `Role` models for the datastore
|
||||
implementation"""
|
||||
raise NotImplementedError(
|
||||
"User datastore does not implement get_models method")
|
||||
|
||||
def _save_model(self, model, **kwargs):
|
||||
raise NotImplementedError(
|
||||
@@ -96,36 +120,79 @@ class UserDatastore(object):
|
||||
return kwargs
|
||||
|
||||
def with_id(self, id):
|
||||
"""Returns a user with the specified ID.
|
||||
|
||||
:param id: User ID"""
|
||||
user = self._do_with_id(id)
|
||||
if user: return user
|
||||
raise security.UserIdNotFoundError()
|
||||
|
||||
def find_user(self, user):
|
||||
"""Returns a user based on the specified identifier.
|
||||
|
||||
:param user: User identifier, usually a username or email address
|
||||
"""
|
||||
user = self._do_find_user(user)
|
||||
if user: return user
|
||||
raise security.UserNotFoundError()
|
||||
|
||||
def find_role(self, role):
|
||||
"""Returns a role based on its name.
|
||||
|
||||
:param role: Role name
|
||||
"""
|
||||
role = self._do_find_role(role)
|
||||
if role: return role
|
||||
raise security.RoleNotFoundError()
|
||||
|
||||
def create_role(self, commit=True, **kwargs):
|
||||
def create_role(self, **kwargs):
|
||||
"""Creates and returns a new role.
|
||||
|
||||
:param name: Role name
|
||||
:param description: Role description
|
||||
"""
|
||||
role = security.Role(**self._prepare_create_role_args(kwargs))
|
||||
return self._save_model(role)
|
||||
|
||||
def create_user(self, commit=True, **kwargs):
|
||||
def create_user(self, **kwargs):
|
||||
"""Creates and returns a new user.
|
||||
|
||||
:param username: Username
|
||||
:param email: Email address
|
||||
:param password: Unencrypted password
|
||||
:param active: The optional active state
|
||||
"""
|
||||
user = security.User(**self._prepare_create_user_args(kwargs))
|
||||
return self._save_model(user)
|
||||
|
||||
def add_role_to_user(self, user, role):
|
||||
"""Adds a role to a user if the user does not have it already. Returns
|
||||
the modified user.
|
||||
|
||||
:param user: A User instance or a user identifier
|
||||
:param role: A Role instance or a role name
|
||||
"""
|
||||
return self._save_model(self._do_add_role(user, role))
|
||||
|
||||
def remove_role_from_user(self, user, role, commit=True):
|
||||
"""Removes a role from a user if the user has the role. Returns the
|
||||
modified user.
|
||||
|
||||
:param user: A User instance or a user identifier
|
||||
:param role: A Role instance or a role name
|
||||
"""
|
||||
return self._save_model(self._do_remove_role(user, role))
|
||||
|
||||
def deactivate_user(self, user):
|
||||
"""Deactivates a user and returns the modified user.
|
||||
|
||||
:param user: A User instance or a user identifier
|
||||
"""
|
||||
return self._save_model(self._do_deactive_user(user))
|
||||
|
||||
def activate_user(self, user, commit=True):
|
||||
"""Activates a user and returns the modified user.
|
||||
|
||||
:param user: A User instance or a user identifier
|
||||
"""
|
||||
return self._save_model(self._do_active_user(user))
|
||||
@@ -1,21 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ext.security.datastore.mongoengine
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains a Flask-Security MongoEngine datastore implementation
|
||||
|
||||
:copyright: (c) 2012 by Matt Wright.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask.ext import security
|
||||
from flask.ext.security import UserMixin, RoleMixin
|
||||
from flask.ext.security.datastore import UserDatastore
|
||||
|
||||
class MongoEngineUserDatastore(UserDatastore):
|
||||
"""MongoEngine datastore"""
|
||||
"""A MongoEngine datastore implementation for Flask-Security. Example:
|
||||
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
from flask import Flask
|
||||
from flask.ext.mongoengine import MongoEngine
|
||||
from flask.ext.security import Security
|
||||
from flask.ext.security.datastore.mongoengine import MongoEngineUserDatastore
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'secret'
|
||||
app.config['MONGODB_DB'] = 'flask_security_example'
|
||||
app.config['MONGODB_HOST'] = 'localhost'
|
||||
app.config['MONGODB_PORT'] = 27017
|
||||
|
||||
db = MongoEngine(app)
|
||||
Security(app, MongoEngineUserDatastore(db))
|
||||
"""
|
||||
|
||||
def get_models(self):
|
||||
db = self.db
|
||||
|
||||
class Role(db.Document, RoleMixin):
|
||||
"""MongoEngine Role model"""
|
||||
|
||||
name = db.StringField(required=True, unique=True, max_length=80)
|
||||
description = db.StringField(max_length=255)
|
||||
|
||||
class User(db.Document, UserMixin):
|
||||
"""MongoEngine User model"""
|
||||
|
||||
username = db.StringField(unique=True, max_length=255)
|
||||
email = db.StringField(unique=True, max_length=255)
|
||||
password = db.StringField(required=True, max_length=120)
|
||||
|
||||
@@ -1,13 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ext.security.datastore.sqlalchemy
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains a Flask-Security SQLAlchemy datastore implementation
|
||||
|
||||
:copyright: (c) 2012 by Matt Wright.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask.ext import security
|
||||
from flask.ext.security import UserMixin, RoleMixin
|
||||
from flask.ext.security.datastore import UserDatastore
|
||||
|
||||
class SQLAlchemyUserDatastore(UserDatastore):
|
||||
"""SQLAlchemy datastore"""
|
||||
"""A SQLAlchemy datastore implementation for Flask-Security. Example:
|
||||
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
from flask import Flask
|
||||
from flask.ext.security import Security
|
||||
from flask.ext.security.datastore.sqlalchemy import SQLAlchemyUserDatastore
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'secret'
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/flask_security_example.sqlite'
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
Security(app, SQLAlchemyUserDatastore(db))
|
||||
"""
|
||||
|
||||
def get_models(self):
|
||||
db = self.db
|
||||
|
||||
@@ -16,6 +37,8 @@ class SQLAlchemyUserDatastore(UserDatastore):
|
||||
db.Column('role_id', db.Integer(), db.ForeignKey('user.id')))
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
"""SQLAlchemy Role model"""
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
name = db.Column(db.String(80), unique=True)
|
||||
description = db.Column(db.String(255))
|
||||
@@ -25,6 +48,8 @@ class SQLAlchemyUserDatastore(UserDatastore):
|
||||
self.description = description
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
"""SQLAlchemy User model"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(255), unique=True)
|
||||
email = db.Column(db.String(255), unique=True)
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ext.security.script
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains commands for use with the Flask-Script extension
|
||||
|
||||
:copyright: (c) 2012 by Matt Wright.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from flask.ext.script import Command, Option
|
||||
|
||||
Reference in New Issue
Block a user