mirror of
https://github.com/wassname/flask-security.git
synced 2026-07-20 12:20:47 +08:00
+1
-1
@@ -7,7 +7,7 @@ python:
|
||||
install:
|
||||
- pip install . --quiet --use-mirrors
|
||||
- "if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install importlib --quiet --use-mirrors; fi"
|
||||
- pip install nose simplejson Flask-SQLAlchemy Flask-MongoEngine Flask-Mail py-bcrypt MySQL-python --quiet --use-mirrors
|
||||
- pip install nose simplejson Flask-SQLAlchemy Flask-MongoEngine Flask-Peewee Flask-Mail py-bcrypt MySQL-python --quiet --use-mirrors
|
||||
|
||||
before_script:
|
||||
- mysql -e 'create database flask_security_test;'
|
||||
|
||||
@@ -49,6 +49,10 @@ Datastores
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: flask_security.datastore.PeeweeUserDatastore
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Signals
|
||||
-------
|
||||
|
||||
+6
-5
@@ -10,7 +10,7 @@ Flask application. They include:
|
||||
4. Basic HTTP authentication
|
||||
5. Token based authentication
|
||||
6. Token based account activation (optional)
|
||||
7. Token based password recovery/resetting (optional)
|
||||
7. Token based password recovery / resetting (optional)
|
||||
8. User registration (optional)
|
||||
9. Login tracking (optional)
|
||||
|
||||
@@ -27,10 +27,11 @@ and libraries. They include:
|
||||
|
||||
Additionally, it assumes you'll be using a common library for your database
|
||||
connections and model definitions. Flask-Security supports the following Flask
|
||||
extensions out of the box for data persistance:
|
||||
extensions out of the box for data persistence:
|
||||
|
||||
1. `Flask-SQLAlchemy <http://packages.python.org/Flask-SQLAlchemy/>`_
|
||||
2. `Flask-MongoEngine <http://packages.python.org/Flask-MongoEngine/>`_
|
||||
1. `Flask-SQLAlchemy <http://pypi.python.org/pypi/flask-sqlalchemy/>`_
|
||||
2. `Flask-MongoEngine <http://pypi.python.org/pypi/flask-mongoengine/>`_
|
||||
3. `Flask-Peewee <http://pypi.python.org/pypi/flask-peewee/>`_
|
||||
|
||||
|
||||
.. include:: contents.rst.inc
|
||||
.. include:: contents.rst.inc
|
||||
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
Models
|
||||
======
|
||||
|
||||
Flask-Security assumes you'll be using libraries such as SQLAlchemy or
|
||||
MongoEngine to define a data model that includes a `User` and `Role` model. The
|
||||
fields on your models must follow a particular convention depending on the
|
||||
functionality your app requires. Aside from this, you're free to add any
|
||||
additional fields to your model(s) if you want. At the bear minimum your `User`
|
||||
and `Role` model should include the following fields:
|
||||
Flask-Security assumes you'll be using libraries such as SQLAlchemy,
|
||||
MongoEngine or Peewee to define a data model that includes a `User` and
|
||||
`Role` model. The fields on your models must follow a particular convention
|
||||
depending on the functionality your app requires. Aside from this, you're
|
||||
free to add any additional fields to your model(s) if you want. At the bear
|
||||
minimum your `User` and `Role` model should include the following fields:
|
||||
|
||||
**User**
|
||||
|
||||
@@ -48,4 +48,4 @@ additional fields:
|
||||
* ``current_login_at``
|
||||
* ``last_login_ip``
|
||||
* ``current_login_ip``
|
||||
* ``login_count``
|
||||
* ``login_count``
|
||||
|
||||
+84
-3
@@ -3,6 +3,7 @@ Quick Start
|
||||
|
||||
- `Basic SQLAlchemy Application <#basic-sqlalchemy-application>`_
|
||||
- `Basic MongoEngine Application <#basic-mongoengine-application>`_
|
||||
- `Basic Peewee Application <#basic-peewee-application>`_
|
||||
- `Mail Configuration <#mail-configuration>`_
|
||||
|
||||
Basic SQLAlchemy Application
|
||||
@@ -21,7 +22,7 @@ SQLAlchemy Application
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following code sample illustrates how to get started as quickly as
|
||||
possible using SQLAlchemy.
|
||||
possible using SQLAlchemy:
|
||||
|
||||
::
|
||||
|
||||
@@ -94,7 +95,9 @@ MongoEngine Application
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following code sample illustrates how to get started as quickly as
|
||||
possible using MongoEngine::
|
||||
possible using MongoEngine:
|
||||
|
||||
::
|
||||
|
||||
from flask import Flask, render_template
|
||||
from flask.ext.mongoengine import MongoEngine
|
||||
@@ -144,6 +147,84 @@ possible using MongoEngine::
|
||||
app.run()
|
||||
|
||||
|
||||
Basic Peewee Application
|
||||
========================
|
||||
|
||||
Peewee Install requirements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
$ mkvirtualenv <your-app-name>
|
||||
$ pip install flask-security flask-peewee
|
||||
|
||||
Peewee Application
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following code sample illustrates how to get started as quickly as
|
||||
possible using Peewee:
|
||||
|
||||
::
|
||||
|
||||
from flask import Flask, render_template
|
||||
from flask_peewee.db import Database
|
||||
from peewee import *
|
||||
from flask.ext.security import Security, PeeweeUserDatastore, \
|
||||
UserMixin, RoleMixin, login_required
|
||||
|
||||
# Create app
|
||||
app = Flask(__name__)
|
||||
app.config['DEBUG'] = True
|
||||
app.config['SECRET_KEY'] = 'super-secret'
|
||||
app.config['DATABASE'] = {
|
||||
'name': 'example.db',
|
||||
'engine': 'peewee.SqliteDatabase',
|
||||
}
|
||||
|
||||
# Create database connection object
|
||||
db = Database(app)
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
name = TextField(unique=True)
|
||||
description = TextField(null=True)
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
email = TextField()
|
||||
password = TextField()
|
||||
active = BooleanField(default=True)
|
||||
confirmed_at = DateTimeField(null=True)
|
||||
|
||||
class UserRoles(db.Model):
|
||||
# Because peewee does not come with built-in many-to-many
|
||||
# relationships, we need this intermediary class to link
|
||||
# user to roles.
|
||||
user = ForeignKeyField(User, related_name='roles')
|
||||
role = ForeignKeyField(Role, related_name='users')
|
||||
name = property(lambda self: self.role.name)
|
||||
description = property(lambda self: self.role.description)
|
||||
|
||||
# Setup Flask-Security
|
||||
user_datastore = PeeweeUserDatastore(db, User, Role, UserRoles)
|
||||
security = Security(app, user_datastore)
|
||||
|
||||
# Create a user to test with
|
||||
@app.before_first_request
|
||||
def create_user():
|
||||
for Model in (Role, User, UserRoles):
|
||||
Model.drop_table(fail_silently=True)
|
||||
Model.create_table(fail_silently=True)
|
||||
user_datastore.create_user(email='matt@nobien.net', password='password')
|
||||
|
||||
# Views
|
||||
@app.route('/')
|
||||
@login_required
|
||||
def home():
|
||||
return render_template('index.html')
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
|
||||
Mail Configuration
|
||||
===================
|
||||
|
||||
@@ -168,4 +249,4 @@ the basic application code in the previous section::
|
||||
|
||||
To learn more about the various Flask-Mail settings to configure it to
|
||||
work with your particular email server configuration, please see the
|
||||
`Flask-Mail documentation <http://packages.python.org/Flask-Mail/>`_.
|
||||
`Flask-Mail documentation <http://packages.python.org/Flask-Mail/>`_.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
__version__ = '1.5.4'
|
||||
|
||||
from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
|
||||
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore
|
||||
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore, PeeweeUserDatastore
|
||||
from .decorators import auth_token_required, http_auth_required, \
|
||||
login_required, roles_accepted, roles_required
|
||||
from .forms import ForgotPasswordForm, LoginForm, RegisterForm, \
|
||||
|
||||
@@ -44,6 +44,15 @@ class MongoEngineDatastore(Datastore):
|
||||
model.delete()
|
||||
|
||||
|
||||
class PeeweeDatastore(Datastore):
|
||||
def put(self, model):
|
||||
model.save()
|
||||
return model
|
||||
|
||||
def delete(self, model):
|
||||
model.delete_instance()
|
||||
|
||||
|
||||
class UserDatastore(object):
|
||||
"""Abstracted user datastore.
|
||||
|
||||
@@ -73,11 +82,11 @@ class UserDatastore(object):
|
||||
return kwargs
|
||||
|
||||
def find_user(self, **kwargs):
|
||||
"""Returns a user matching the provided paramters."""
|
||||
"""Returns a user matching the provided parameters."""
|
||||
raise NotImplementedError
|
||||
|
||||
def find_role(self, **kwargs):
|
||||
"""Returns a role matching the provided paramters."""
|
||||
def find_role(self, name):
|
||||
"""Returns a role matching the provided name."""
|
||||
raise NotImplementedError
|
||||
|
||||
def add_role_to_user(self, user, role):
|
||||
@@ -137,6 +146,13 @@ class UserDatastore(object):
|
||||
role = self.role_model(**kwargs)
|
||||
return self.put(role)
|
||||
|
||||
def find_or_create_role(self, name, **kwargs):
|
||||
"""Returns a role matching the given name or creates it with any
|
||||
additionally provided parameters
|
||||
"""
|
||||
kwargs["name"] = name
|
||||
return self.find_role(name) or self.create_role(**kwargs)
|
||||
|
||||
def create_user(self, **kwargs):
|
||||
"""Creates and returns a new user from the given parameters."""
|
||||
|
||||
@@ -179,3 +195,66 @@ class MongoEngineUserDatastore(MongoEngineDatastore, UserDatastore):
|
||||
|
||||
def find_role(self, role):
|
||||
return self.role_model.objects(name=role).first()
|
||||
|
||||
|
||||
class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
|
||||
"""A PeeweeD datastore implementation for Flask-Security that assumes
|
||||
the use of the Flask-Peewee extension.
|
||||
|
||||
:param user_model: A user model class definition
|
||||
:param role_model: A role model class definition
|
||||
:param role_link: A model implementing the many-to-many user-role relation
|
||||
"""
|
||||
def __init__(self, db, user_model, role_model, role_link):
|
||||
PeeweeDatastore.__init__(self, db)
|
||||
UserDatastore.__init__(self, user_model, role_model)
|
||||
self.UserRole = role_link
|
||||
|
||||
def find_user(self, **kwargs):
|
||||
try:
|
||||
return self.user_model.filter(**kwargs).get()
|
||||
except self.user_model.DoesNotExist:
|
||||
return None
|
||||
|
||||
def find_role(self, role):
|
||||
try:
|
||||
return self.role_model.filter(name=role).get()
|
||||
except self.role_model.DoesNotExist:
|
||||
return None
|
||||
|
||||
def create_user(self, **kwargs):
|
||||
"""Creates and returns a new user from the given parameters."""
|
||||
roles = kwargs.pop('roles', [])
|
||||
user = self.user_model(**self._prepare_create_user_args(**kwargs))
|
||||
user = self.put(user)
|
||||
for role in roles:
|
||||
self.add_role_to_user(user, role)
|
||||
return user
|
||||
|
||||
|
||||
def add_role_to_user(self, user, role):
|
||||
"""Adds a role tp a user
|
||||
|
||||
:param user: The user to manipulate
|
||||
:param role: The role to add to the user
|
||||
"""
|
||||
user, role = self._prepare_role_modify_args(user, role)
|
||||
if self.UserRole.select().where(self.UserRole.user==user, self.UserRole.role==role).count():
|
||||
return False
|
||||
else:
|
||||
self.UserRole.create(user=user, role=role)
|
||||
return True
|
||||
|
||||
def remove_role_from_user(self, user, role):
|
||||
"""Removes a role from a user
|
||||
|
||||
:param user: The user to manipulate
|
||||
:param role: The role to remove from the user
|
||||
"""
|
||||
user, role = self._prepare_role_modify_args(user, role)
|
||||
if self.UserRole.select().where(self.UserRole.user==user, self.UserRole.role==role).count():
|
||||
self.UserRole.delete().where(self.UserRole.user==user, self.UserRole.role==role)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ setup(
|
||||
'nose',
|
||||
'Flask-SQLAlchemy',
|
||||
'Flask-MongoEngine',
|
||||
'Flask-Peewee',
|
||||
'py-bcrypt',
|
||||
'simplejson'
|
||||
],
|
||||
|
||||
@@ -224,6 +224,13 @@ class MongoEngineSecurityTests(DefaultSecurityTests):
|
||||
return create_app(auth_config, **kwargs)
|
||||
|
||||
|
||||
class PeeweeSecurityTests(DefaultSecurityTests):
|
||||
|
||||
def _create_app(self, auth_config, **kwargs):
|
||||
from tests.test_app.peewee_app import create_app
|
||||
return create_app(auth_config, **kwargs)
|
||||
|
||||
|
||||
class DefaultDatastoreTests(SecurityTest):
|
||||
|
||||
def test_add_role_to_user(self):
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.pop(0)
|
||||
sys.path.insert(0, os.getcwd())
|
||||
|
||||
from flask_peewee.db import Database
|
||||
from peewee import *
|
||||
from flask.ext.security import Security, UserMixin, RoleMixin, \
|
||||
PeeweeUserDatastore
|
||||
|
||||
from tests.test_app import create_app as create_base_app, populate_data, \
|
||||
add_context_processors
|
||||
|
||||
def create_app(config, **kwargs):
|
||||
app = create_base_app(config)
|
||||
app.config['DATABASE'] = {
|
||||
'name': 'example2.db',
|
||||
'engine': 'peewee.SqliteDatabase',
|
||||
}
|
||||
db = Database(app)
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
name = TextField(unique=True)
|
||||
description = TextField(null=True)
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
email = TextField()
|
||||
password = TextField()
|
||||
last_login_at = DateTimeField(null=True)
|
||||
current_login_at = DateTimeField(null=True)
|
||||
last_login_ip = TextField(null=True)
|
||||
current_login_ip = TextField(null=True)
|
||||
login_count = IntegerField(null=True)
|
||||
active = BooleanField(default=True)
|
||||
confirmed_at = DateTimeField(null=True)
|
||||
|
||||
class UserRoles(db.Model):
|
||||
""" Peewee does not have built-in many-to-many support, so we have to
|
||||
create this mapping class to link users to roles."""
|
||||
user = ForeignKeyField(User, related_name='roles')
|
||||
role = ForeignKeyField(Role, related_name='users')
|
||||
name = property(lambda self: self.role.name)
|
||||
description = property(lambda self: self.role.description)
|
||||
|
||||
@app.before_first_request
|
||||
def before_first_request():
|
||||
for Model in (Role, User, UserRoles):
|
||||
Model.drop_table(fail_silently=True)
|
||||
Model.create_table(fail_silently=True)
|
||||
populate_data(app.config.get('USER_COUNT', None))
|
||||
|
||||
app.security = Security(app, datastore=PeeweeUserDatastore(db, User, Role, UserRoles), **kwargs)
|
||||
|
||||
add_context_processors(app.security)
|
||||
|
||||
return app
|
||||
|
||||
if __name__ == '__main__':
|
||||
create_app({}).run()
|
||||
Reference in New Issue
Block a user