mirror of
https://github.com/wassname/flask-security.git
synced 2026-08-07 11:22:21 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c9bc541f9 | ||
|
|
f7147648ba | ||
|
|
30b72cb7f4 | ||
|
|
ab08abcaf9 | ||
|
|
0227d987fe | ||
|
|
5df01af720 | ||
|
|
0acf4bca0d |
@@ -3,6 +3,14 @@ Flask-Security Changelog
|
|||||||
|
|
||||||
Here you can see the full list of changes between each Flask-Security release.
|
Here you can see the full list of changes between each Flask-Security release.
|
||||||
|
|
||||||
|
Version 1.2.2
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Released April 27th, 2012
|
||||||
|
|
||||||
|
- Fixed bug where `roles_required` and `roles_accepted` did not pass the next
|
||||||
|
argument to the login view
|
||||||
|
|
||||||
Version 1.2.1
|
Version 1.2.1
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -18,7 +18,7 @@ import sys, os
|
|||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
sys.path.insert(0, os.path.abspath('..'))
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
sys.path.append(os.path.abspath('_themes'))
|
sys.path.append(os.path.abspath('_themes'))
|
||||||
from flask_security import __version__
|
#from setup import __version__
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ copyright = u'2012, Matt Wright'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = __version__
|
version = '1.2.1'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = version
|
release = version
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -85,7 +85,7 @@ First thing you'll want to do is setup your application and datastore::
|
|||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
from flask.ext.security import (User, Security, LoginForm, login_required,
|
from flask.ext.security import (User, Security, LoginForm, login_required,
|
||||||
roles_accepted, user_datastore)
|
roles_accepted, user_datastore)
|
||||||
from flask.ext.security.datastore.sqlalchemy import SQLAlchemyUserDataStore
|
from flask.ext.security.datastore.sqlalchemy import SQLAlchemyUserDatastore
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SECRET_KEY'] = 'secret'
|
app.config['SECRET_KEY'] = 'secret'
|
||||||
@@ -94,14 +94,14 @@ First thing you'll want to do is setup your application and datastore::
|
|||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
Security(app, SQLAlchemyUserDatastore(db))
|
Security(app, SQLAlchemyUserDatastore(db))
|
||||||
|
|
||||||
You'll probably want to at least one user to the database to test this out, so
|
You'll probably want to at least one user to the database to test this out.
|
||||||
you can add something such as the following to quickly add an initial user::
|
There are many ways to do this, but this is a quick and dirty way to do it::
|
||||||
|
|
||||||
@app.before_first_request
|
@app.before_first_request
|
||||||
def before_first_request():
|
def before_first_request():
|
||||||
user_datastore.create_role(name='admin')
|
user_datastore.create_role(name='admin')
|
||||||
user_datastore.create_user(username='matt', email='matt@something.com',
|
user_datastore.create_user(username='matt', email='matt@something.com',
|
||||||
password='password', roles['admin'])
|
password='password', roles=['admin'])
|
||||||
|
|
||||||
Next you'll want to setup your login screen. Setup your view::
|
Next you'll want to setup your login screen. Setup your view::
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
{{ form.username.label }} {{ form.username }}<br/>
|
{{ form.username.label }} {{ form.username }}<br/>
|
||||||
{{ form.password.label }} {{ form.password }}<br/>
|
{{ form.password.label }} {{ form.password }}<br/>
|
||||||
{{ form.remember.label }} {{ form.remember }}<br/>
|
{{ form.remember.label }} {{ form.remember }}<br/>
|
||||||
|
{{ form.next }}
|
||||||
{{ form.submit }}
|
{{ form.submit }}
|
||||||
</form>
|
</form>
|
||||||
<p>{{ content }}</p>
|
<p>{{ content }}</p>
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
:license: MIT, see LICENSE for more details.
|
:license: MIT, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = '1.2.1'
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -22,7 +20,8 @@ from flask import (current_app, Blueprint, flash, redirect, request,
|
|||||||
|
|
||||||
from flask.ext.login import (AnonymousUser as AnonymousUserBase,
|
from flask.ext.login import (AnonymousUser as AnonymousUserBase,
|
||||||
UserMixin as BaseUserMixin, LoginManager, login_required, login_user,
|
UserMixin as BaseUserMixin, LoginManager, login_required, login_user,
|
||||||
logout_user, current_user, user_logged_in, user_logged_out)
|
logout_user, current_user, user_logged_in, user_logged_out,
|
||||||
|
login_url)
|
||||||
|
|
||||||
from flask.ext.principal import (Identity, Principal, RoleNeed, UserNeed,
|
from flask.ext.principal import (Identity, Principal, RoleNeed, UserNeed,
|
||||||
Permission, AnonymousIdentity, identity_changed, identity_loaded)
|
Permission, AnonymousIdentity, identity_changed, identity_loaded)
|
||||||
@@ -149,7 +148,8 @@ def roles_required(*args):
|
|||||||
@wraps(fn)
|
@wraps(fn)
|
||||||
def decorated_view(*args, **kwargs):
|
def decorated_view(*args, **kwargs):
|
||||||
if not current_user.is_authenticated():
|
if not current_user.is_authenticated():
|
||||||
return redirect(current_app.config[LOGIN_VIEW_KEY])
|
return redirect(
|
||||||
|
login_url(current_app.config[LOGIN_VIEW_KEY], request.url))
|
||||||
|
|
||||||
if perm.can():
|
if perm.can():
|
||||||
return fn(*args, **kwargs)
|
return fn(*args, **kwargs)
|
||||||
@@ -183,7 +183,8 @@ def roles_accepted(*args):
|
|||||||
@wraps(fn)
|
@wraps(fn)
|
||||||
def decorated_view(*args, **kwargs):
|
def decorated_view(*args, **kwargs):
|
||||||
if not current_user.is_authenticated():
|
if not current_user.is_authenticated():
|
||||||
return redirect(current_app.config[LOGIN_VIEW_KEY])
|
return redirect(
|
||||||
|
login_url(current_app.config[LOGIN_VIEW_KEY], request.url))
|
||||||
|
|
||||||
for perm in perms:
|
for perm in perms:
|
||||||
if perm.can():
|
if perm.can():
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Flask-Security
|
Flask-Security
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Flask-Security is a Flask extension that aims to add quick and simple security
|
Flask-Security is a Flask extension that aims to add quick and simple security
|
||||||
via Flask-Login, Flask-Principal, Flask-WTF, and passlib.
|
via Flask-Login, Flask-Principal, Flask-WTF, and passlib.
|
||||||
|
|
||||||
Links
|
Links
|
||||||
@@ -12,12 +12,12 @@ Links
|
|||||||
<https://github.com/mattupstate/flask-security/raw/develop#egg=Flask-Security-dev>`_
|
<https://github.com/mattupstate/flask-security/raw/develop#egg=Flask-Security-dev>`_
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from flask_security import __version__
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='Flask-Security',
|
name='Flask-Security',
|
||||||
version=__version__,
|
version='1.2.2',
|
||||||
url='https://github.com/mattupstate/flask-security',
|
url='https://github.com/mattupstate/flask-security',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
author='Matthew Wright',
|
author='Matthew Wright',
|
||||||
|
|||||||
+39
-34
@@ -1,108 +1,113 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from example import app
|
from example import app
|
||||||
|
|
||||||
|
|
||||||
class SecurityTest(unittest.TestCase):
|
class SecurityTest(unittest.TestCase):
|
||||||
|
|
||||||
AUTH_CONFIG = None
|
AUTH_CONFIG = None
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(SecurityTest, self).setUp()
|
super(SecurityTest, self).setUp()
|
||||||
|
|
||||||
self.app = self._create_app(self.AUTH_CONFIG or None)
|
self.app = self._create_app(self.AUTH_CONFIG or None)
|
||||||
self.app.debug = False
|
self.app.debug = False
|
||||||
self.app.config['TESTING'] = True
|
self.app.config['TESTING'] = True
|
||||||
|
|
||||||
self.client = self.app.test_client()
|
self.client = self.app.test_client()
|
||||||
|
|
||||||
def _create_app(self, auth_config):
|
def _create_app(self, auth_config):
|
||||||
return app.create_sqlalchemy_app(auth_config)
|
return app.create_sqlalchemy_app(auth_config)
|
||||||
|
|
||||||
def _get(self, route, content_type=None, follow_redirects=None):
|
def _get(self, route, content_type=None, follow_redirects=None):
|
||||||
return self.client.get(route, follow_redirects=follow_redirects,
|
return self.client.get(route, follow_redirects=follow_redirects,
|
||||||
content_type=content_type or 'text/html')
|
content_type=content_type or 'text/html')
|
||||||
|
|
||||||
def _post(self, route, data=None, content_type=None, follow_redirects=True):
|
def _post(self, route, data=None, content_type=None, follow_redirects=True):
|
||||||
return self.client.post(route, data=data,
|
return self.client.post(route, data=data,
|
||||||
follow_redirects=follow_redirects,
|
follow_redirects=follow_redirects,
|
||||||
content_type=content_type or 'text/html')
|
content_type=content_type or 'text/html')
|
||||||
|
|
||||||
|
|
||||||
def authenticate(self, username, password, endpoint=None):
|
def authenticate(self, username, password, endpoint=None):
|
||||||
data = dict(username=username, password=password)
|
data = dict(username=username, password=password)
|
||||||
return self._post(endpoint or '/auth', data=data,
|
return self._post(endpoint or '/auth', data=data,
|
||||||
content_type='application/x-www-form-urlencoded')
|
content_type='application/x-www-form-urlencoded')
|
||||||
|
|
||||||
def logout(self, endpoint=None):
|
def logout(self, endpoint=None):
|
||||||
return self._get(endpoint or '/logout', follow_redirects=True)
|
return self._get(endpoint or '/logout', follow_redirects=True)
|
||||||
|
|
||||||
|
|
||||||
class DefaultSecurityTests(SecurityTest):
|
class DefaultSecurityTests(SecurityTest):
|
||||||
|
|
||||||
def test_login_view(self):
|
def test_login_view(self):
|
||||||
r = self._get('/login')
|
r = self._get('/login')
|
||||||
assert 'Login Page' in r.data
|
assert 'Login Page' in r.data
|
||||||
|
|
||||||
def test_authenticate(self):
|
def test_authenticate(self):
|
||||||
r = self.authenticate("matt", "password")
|
r = self.authenticate("matt", "password")
|
||||||
assert 'Home Page' in r.data
|
assert 'Home Page' in r.data
|
||||||
|
|
||||||
def test_unprovided_username(self):
|
def test_unprovided_username(self):
|
||||||
r = self.authenticate("", "password")
|
r = self.authenticate("", "password")
|
||||||
assert "Username not provided" in r.data
|
assert "Username not provided" in r.data
|
||||||
|
|
||||||
def test_unprovided_password(self):
|
def test_unprovided_password(self):
|
||||||
r = self.authenticate("matt", "")
|
r = self.authenticate("matt", "")
|
||||||
assert "Password not provided" in r.data
|
assert "Password not provided" in r.data
|
||||||
|
|
||||||
def test_invalid_user(self):
|
def test_invalid_user(self):
|
||||||
r = self.authenticate("bogus", "password")
|
r = self.authenticate("bogus", "password")
|
||||||
assert "Specified user does not exist" in r.data
|
assert "Specified user does not exist" in r.data
|
||||||
|
|
||||||
def test_bad_password(self):
|
def test_bad_password(self):
|
||||||
r = self.authenticate("matt", "bogus")
|
r = self.authenticate("matt", "bogus")
|
||||||
assert "Password does not match" in r.data
|
assert "Password does not match" in r.data
|
||||||
|
|
||||||
def test_inactive_user(self):
|
def test_inactive_user(self):
|
||||||
r = self.authenticate("tiya", "password")
|
r = self.authenticate("tiya", "password")
|
||||||
assert "Inactive user" in r.data
|
assert "Inactive user" in r.data
|
||||||
|
|
||||||
def test_logout(self):
|
def test_logout(self):
|
||||||
self.authenticate("matt", "password")
|
self.authenticate("matt", "password")
|
||||||
r = self.logout()
|
r = self.logout()
|
||||||
assert 'Home Page' in r.data
|
assert 'Home Page' in r.data
|
||||||
|
|
||||||
def test_unauthorized_access(self):
|
def test_unauthorized_access(self):
|
||||||
r = self._get('/profile', follow_redirects=True)
|
r = self._get('/profile', follow_redirects=True)
|
||||||
assert 'Please log in to access this page' in r.data
|
assert 'Please log in to access this page' in r.data
|
||||||
|
|
||||||
def test_authorized_access(self):
|
def test_authorized_access(self):
|
||||||
self.authenticate("matt", "password")
|
self.authenticate("matt", "password")
|
||||||
r = self._get("/profile")
|
r = self._get("/profile")
|
||||||
assert 'profile' in r.data
|
assert 'profile' in r.data
|
||||||
|
|
||||||
def test_valid_admin_role(self):
|
def test_valid_admin_role(self):
|
||||||
self.authenticate("matt", "password")
|
self.authenticate("matt", "password")
|
||||||
r = self._get("/admin")
|
r = self._get("/admin")
|
||||||
assert 'Admin Page' in r.data
|
assert 'Admin Page' in r.data
|
||||||
|
|
||||||
def test_invalid_admin_role(self):
|
def test_invalid_admin_role(self):
|
||||||
self.authenticate("joe", "password")
|
self.authenticate("joe", "password")
|
||||||
r = self._get("/admin", follow_redirects=True)
|
r = self._get("/admin", follow_redirects=True)
|
||||||
assert 'Home Page' in r.data
|
assert 'Home Page' in r.data
|
||||||
|
|
||||||
def test_roles_accepted(self):
|
def test_roles_accepted(self):
|
||||||
for user in ("matt", "joe"):
|
for user in ("matt", "joe"):
|
||||||
self.authenticate(user, "password")
|
self.authenticate(user, "password")
|
||||||
r = self._get("/admin_or_editor")
|
r = self._get("/admin_or_editor")
|
||||||
self.assertIn('Admin or Editor Page', r.data)
|
self.assertIn('Admin or Editor Page', r.data)
|
||||||
self.logout()
|
self.logout()
|
||||||
|
|
||||||
self.authenticate("jill", "password")
|
self.authenticate("jill", "password")
|
||||||
r = self._get("/admin_or_editor", follow_redirects=True)
|
r = self._get("/admin_or_editor", follow_redirects=True)
|
||||||
self.assertIn('Home Page', r.data)
|
self.assertIn('Home Page', r.data)
|
||||||
|
|
||||||
|
def test_unauthenticated_role_required(self):
|
||||||
|
r = self._get('/admin', follow_redirects=True)
|
||||||
|
self.assertIn('<input id="next"', r.data)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfiguredSecurityTests(SecurityTest):
|
||||||
|
|
||||||
class ConfiguredSecurityTests(SecurityTest):
|
|
||||||
|
|
||||||
AUTH_CONFIG = {
|
AUTH_CONFIG = {
|
||||||
'SECURITY_PASSWORD_HASH': 'bcrypt',
|
'SECURITY_PASSWORD_HASH': 'bcrypt',
|
||||||
'SECURITY_USER_DATASTORE': 'custom_datastore_name',
|
'SECURITY_USER_DATASTORE': 'custom_datastore_name',
|
||||||
@@ -112,22 +117,22 @@ class ConfiguredSecurityTests(SecurityTest):
|
|||||||
'SECURITY_POST_LOGIN': '/post_login',
|
'SECURITY_POST_LOGIN': '/post_login',
|
||||||
'SECURITY_POST_LOGOUT': '/post_logout'
|
'SECURITY_POST_LOGOUT': '/post_logout'
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_login_view(self):
|
def test_login_view(self):
|
||||||
r = self._get('/custom_login')
|
r = self._get('/custom_login')
|
||||||
assert "Custom Login Page" in r.data
|
assert "Custom Login Page" in r.data
|
||||||
|
|
||||||
def test_authenticate(self):
|
def test_authenticate(self):
|
||||||
r = self.authenticate("matt", "password", endpoint="/custom_auth")
|
r = self.authenticate("matt", "password", endpoint="/custom_auth")
|
||||||
assert 'Post Login' in r.data
|
assert 'Post Login' in r.data
|
||||||
|
|
||||||
def test_logout(self):
|
def test_logout(self):
|
||||||
self.authenticate("matt", "password", endpoint="/custom_auth")
|
self.authenticate("matt", "password", endpoint="/custom_auth")
|
||||||
r = self.logout(endpoint="/custom_logout")
|
r = self.logout(endpoint="/custom_logout")
|
||||||
assert 'Post Logout' in r.data
|
assert 'Post Logout' in r.data
|
||||||
|
|
||||||
|
|
||||||
class MongoEngineSecurityTests(DefaultSecurityTests):
|
class MongoEngineSecurityTests(DefaultSecurityTests):
|
||||||
|
|
||||||
def _create_app(self, auth_config):
|
def _create_app(self, auth_config):
|
||||||
return app.create_mongoengine_app(auth_config)
|
return app.create_mongoengine_app(auth_config)
|
||||||
|
|||||||
Reference in New Issue
Block a user