Compare commits

...
7 Commits
7 changed files with 63 additions and 48 deletions
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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::
+1
View File
@@ -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>
+6 -5
View File
@@ -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():
+3 -3
View File
@@ -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
View File
@@ -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)