This commit is contained in:
Bruno Rocha
2014-01-24 14:06:03 -02:00
19 changed files with 260 additions and 179 deletions
+2
View File
@@ -31,3 +31,5 @@ env/
#Editor temporaries
*~
*.db
+3 -4
View File
@@ -3,14 +3,13 @@ language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "pypy"
install:
- pip install . --quiet
- "if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install importlib --quiet --use-mirrors; fi"
- pip install nose simplejson Flask-SQLAlchemy Flask-MongoEngine Flask-Peewee py-bcrypt MySQL-python --quiet
before_script:
- mysql -e 'create database flask_security_test;'
- pip install nose simplejson Flask-SQLAlchemy Flask-MongoEngine Flask-Peewee bcrypt --quiet
services:
- mongodb
+35
View File
@@ -3,6 +3,41 @@ Flask-Security Changelog
Here you can see the full list of changes between each Flask-Security release.
Version 1.7.1
-------------
Released January 14th 2014
- Fixed a bug where passwords would fail to verify when specifying a password hash algorithm
Version 1.7.0
-------------
Released January 10th 2014
- Python 3.3 support!
- Dependency updates
- Fixed a bug when `SECURITY_LOGIN_WITHOUT_CONFIRMATION = True` did not allow users to log in
- Added `SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL` configuraiton option to optionally send password reset notice emails
- Add documentation for `@security.send_mail_task`
- Move to `request.get_json` as `request.json` is now deprecated in Flask
- Fixed a bug when using AJAX to change a user's password
- Added documentation for select functions in the `flask_security.utils` module
- Fixed a bug in `flask_security.forms.NextFormMixin`
- Added `CHANGE_PASSWORD_TEMPLATE` configuration option to optionally specify a different change password template
- Added the ability to specify addtional fields on the user model to be used for identifying the user via the `USER_IDENTITY_ATTRIBUTES` configuration option
- An error is now shown if a user tries to change their password and the password is the same as before. The message can be customed with the `SECURITY_MSG_PASSWORD_IS_SAME` configuration option
- Fixed a bug in `MongoEngineUserDatastore` where user model would not be updated when using the `add_role_to_user` method
- Added `SECURITY_SEND_PASSWORD_CHANGE_EMAIL` configuration option to optionally disable password change email from being sent
- Fixed a bug in the `find_or_create_role` method of the PeeWee datastore
- Removed pypy tests
- Fixed some tests
- Include CHANGES and LICENSE in MANIFEST.in
- A bit of documentation cleanup
- A bit of code cleanup including removal of unnecessary utcnow call and simplification of get_max_age method
Version 1.6.9
-------------
+1 -1
View File
@@ -49,7 +49,7 @@ copyright = u'2012, Matt Wright'
# built documents.
#
# The short X.Y version.
version = '1.6.9'
version = '1.7.1'
# The full version, including alpha/beta/rc tags.
release = version
+1 -1
View File
@@ -55,7 +55,7 @@ authentication endpoint. A successful call to this endpoint will return the
user's ID and their authentication token. This token can be used in subsequent
requests to protected resources. The auth token is supplied in the request
through an HTTP header or query string parameter. By default the HTTP header
name is `X-Auth-Token` and the default query string parameter name is
name is `Authentication-Token` and the default query string parameter name is
`auth_token`. Authentication tokens are generated using the user's password.
Thus if the user changes his or her password their existing authentication token
will become invalid. A new token will need to be retrieved using the user's new
+1 -1
View File
@@ -10,7 +10,7 @@
:license: MIT, see LICENSE for more details.
"""
__version__ = '1.6.9'
__version__ = '1.7.1'
from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore, PeeweeUserDatastore
+3 -2
View File
@@ -19,7 +19,7 @@ from passlib.context import CryptContext
from werkzeug.datastructures import ImmutableList
from werkzeug.local import LocalProxy
from .utils import config_value as cv, get_config, md5, url_for_security
from .utils import config_value as cv, get_config, md5, url_for_security, string_types
from .views import create_blueprint
from .forms import LoginForm, ConfirmRegisterForm, RegisterForm, \
ForgotPasswordForm, ChangePasswordForm, ResetPasswordForm, \
@@ -249,6 +249,7 @@ def _context_processor():
class RoleMixin(object):
"""Mixin for `Role` model definitions"""
def __eq__(self, other):
return (self.name == other or
self.name == getattr(other, 'name', None))
@@ -273,7 +274,7 @@ class UserMixin(BaseUserMixin):
"""Returns `True` if the user identifies with the specified role.
:param role: A role name or `Role` instance"""
if isinstance(role, basestring):
if isinstance(role, string_types):
return role in (role.name for role in self.roles)
else:
return role in self.roles
+6 -5
View File
@@ -9,7 +9,7 @@
:license: MIT, see LICENSE for more details.
"""
from .utils import get_identity_attributes
from .utils import get_identity_attributes, string_types
class Datastore(object):
@@ -68,9 +68,9 @@ class UserDatastore(object):
self.role_model = role_model
def _prepare_role_modify_args(self, user, role):
if isinstance(user, basestring):
if isinstance(user, string_types):
user = self.find_user(email=user)
if isinstance(role, basestring):
if isinstance(role, string_types):
role = self.find_role(role)
return user, role
@@ -105,6 +105,7 @@ class UserDatastore(object):
user, role = self._prepare_role_modify_args(user, role)
if role not in user.roles:
user.roles.append(role)
self.put(user)
return True
return False
@@ -161,8 +162,8 @@ class UserDatastore(object):
def create_user(self, **kwargs):
"""Creates and returns a new user from the given parameters."""
user = self.user_model(**self._prepare_create_user_args(**kwargs))
kwargs = self._prepare_create_user_args(**kwargs)
user = self.user_model(**kwargs)
return self.put(user)
def delete_user(self, user):
+7 -6
View File
@@ -10,9 +10,10 @@
"""
import inspect
import urlparse
import flask_wtf as wtf
try:
from urlparse import urlsplit
except ImportError:
from urllib.parse import urlsplit
from flask import request, current_app
from flask_wtf import Form as BaseForm
@@ -22,7 +23,7 @@ from flask_login import current_user
from werkzeug.local import LocalProxy
from .confirmable import requires_confirmation
from .utils import verify_and_update_password, get_message, encrypt_password, config_value
from .utils import verify_and_update_password, get_message, config_value
# Convenient reference
_datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)
@@ -137,8 +138,8 @@ class NextFormMixin():
def validate_next(self, field):
if field.data:
url_next = urlparse.urlsplit(field.data)
url_base = urlparse.urlsplit(request.host_url)
url_next = urlsplit(field.data)
url_base = urlsplit(request.host_url)
if url_next.netloc and url_next.netloc != url_base.netloc:
field.data = ''
raise ValidationError(get_message('INVALID_REDIRECT')[0])
+10 -8
View File
@@ -8,6 +8,8 @@
:copyright: (c) 2012 by Matt Wright.
:license: MIT, see LICENSE for more details.
"""
from __future__ import print_function
try:
import simplejson as json
except ImportError:
@@ -26,7 +28,7 @@ _datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)
def pprint(obj):
print json.dumps(obj, sort_keys=True, indent=4)
print(json.dumps(obj, sort_keys=True, indent=4))
def commit(fn):
@@ -59,11 +61,11 @@ class CreateUserCommand(Command):
if form.validate():
kwargs['password'] = encrypt_password(kwargs['password'])
_datastore.create_user(**kwargs)
print 'User created successfully.'
print('User created successfully.')
kwargs['password'] = '****'
pprint(kwargs)
else:
print 'Error creating user'
print('Error creating user')
pprint(form.errors)
@@ -78,7 +80,7 @@ class CreateRoleCommand(Command):
@commit
def run(self, **kwargs):
_datastore.create_role(**kwargs)
print 'Role "%(name)s" created successfully.' % kwargs
print('Role "%(name)s" created successfully.' % kwargs)
class _RoleCommand(Command):
@@ -94,7 +96,7 @@ class AddRoleCommand(_RoleCommand):
@commit
def run(self, user_identifier, role_name):
_datastore.add_role_to_user(user_identifier, role_name)
print "Role '%s' added to user '%s' successfully" % (role_name, user_identifier)
print("Role '%s' added to user '%s' successfully" % (role_name, user_identifier))
class RemoveRoleCommand(_RoleCommand):
@@ -103,7 +105,7 @@ class RemoveRoleCommand(_RoleCommand):
@commit
def run(self, user_identifier, role_name):
_datastore.remove_role_from_user(user_identifier, role_name)
print "Role '%s' removed from user '%s' successfully" % (role_name, user_identifier)
print("Role '%s' removed from user '%s' successfully" % (role_name, user_identifier))
class _ToggleActiveCommand(Command):
@@ -118,7 +120,7 @@ class DeactivateUserCommand(_ToggleActiveCommand):
@commit
def run(self, user_identifier):
_datastore.deactivate_user(user_identifier)
print "User '%s' has been deactivated" % user_identifier
print("User '%s' has been deactivated" % user_identifier)
class ActivateUserCommand(_ToggleActiveCommand):
@@ -127,4 +129,4 @@ class ActivateUserCommand(_ToggleActiveCommand):
@commit
def run(self, user_identifier):
_datastore.activate_user(user_identifier)
print "User '%s' has been activated" % user_identifier
print("User '%s' has been activated" % user_identifier)
-1
View File
@@ -11,7 +11,6 @@
import blinker
signals = blinker.Namespace()
user_registered = signals.signal("user-registered")
+28 -11
View File
@@ -14,6 +14,7 @@ import blinker
import functools
import hashlib
import hmac
import sys
from contextlib import contextmanager
from datetime import datetime, timedelta
@@ -37,6 +38,15 @@ _datastore = LocalProxy(lambda: _security.datastore)
_pwd_context = LocalProxy(lambda: _security.pwd_context)
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
text_type = str
else:
string_types = basestring,
text_type = unicode
def login_user(user, remember=None):
"""Performs the login routine.
@@ -85,16 +95,13 @@ def get_hmac(password):
:param password: The password to sign
"""
if _security.password_hash == 'plaintext':
return password
if _security.password_salt is None:
raise RuntimeError(
'The configuration value `SECURITY_PASSWORD_SALT` must '
'not be None when the value of `SECURITY_PASSWORD_HASH` is '
'set to "%s"' % _security.password_hash)
h = hmac.new(_security.password_salt, password.encode('utf-8'), hashlib.sha512)
h = hmac.new(_security.password_salt.encode('utf-8'), password.encode('utf-8'), hashlib.sha512)
return base64.b64encode(h.digest())
@@ -104,7 +111,7 @@ def verify_password(password, password_hash):
:param password: A plaintext password to verify
:param password_hash: The expected hash value of the password (usually form your database)
"""
return _pwd_context.verify(get_hmac(password), password_hash)
return _pwd_context.verify(encrypt_password(password), password_hash)
def verify_and_update_password(password, user):
@@ -114,7 +121,10 @@ def verify_and_update_password(password, user):
:param password: A plaintext password to verify
:param user: The user to verify against
"""
verified, new_password = _pwd_context.verify_and_update(get_hmac(password), user.password)
if _security.password_hash != 'plaintext':
password = get_hmac(password)
verified, new_password = _pwd_context.verify_and_update(password, user.password)
if verified and new_password:
user.password = new_password
_datastore.put(user)
@@ -126,11 +136,14 @@ def encrypt_password(password):
:param password: The plaintext passwrod to encrypt
"""
return _pwd_context.encrypt(get_hmac(password))
if _security.password_hash == 'plaintext':
return password
signed = get_hmac(password).decode('ascii')
return _pwd_context.encrypt(signed)
def md5(data):
return hashlib.md5(data).hexdigest()
return hashlib.md5(data.encode('ascii')).hexdigest()
def do_flash(message, category=None):
@@ -305,6 +318,10 @@ def get_token_status(token, serializer, max_age=None):
expired = True
except BadSignature:
invalid = True
except TypeError:
invalid = True
except ValueError:
invalid = True
if data:
user = _datastore.find_user(id=data[0])
@@ -408,19 +425,19 @@ class CaptureSignals(object):
self._records[signal].append((args, kwargs))
def __enter__(self):
for signal, receiver in self._receivers.iteritems():
for signal, receiver in self._receivers.items():
signal.connect(receiver)
return self
def __exit__(self, type, value, traceback):
for signal, receiver in self._receivers.iteritems():
for signal, receiver in self._receivers.items():
signal.disconnect(receiver)
def signals_sent(self):
"""Return a set of the signals sent.
:rtype: list of blinker `NamedSignals`.
"""
return set([signal for signal, _ in self._records.iteritems() if self._records[signal]])
return set([signal for signal, _ in self._records.items() if self._records[signal]])
def capture_signals():
+1
View File
@@ -98,6 +98,7 @@ def logout():
get_url(_security.post_logout_view))
@anonymous_user_required
def register():
"""View function which handles a registration request."""
+8 -9
View File
@@ -20,7 +20,7 @@ from setuptools import setup
setup(
name='Flask-Security',
version='1.6.9',
version='1.7.1',
url='https://github.com/mattupstate/flask-security',
license='MIT',
author='Matt Wright',
@@ -34,13 +34,12 @@ setup(
include_package_data=True,
platforms='any',
install_requires=[
'Flask>=0.9',
'Flask-Login>=0.2.3',
'Flask-Mail>=0.7.3',
'Flask-Principal>=0.3.3',
'Flask-WTF>=0.8',
'itsdangerous>=0.17',
'passlib>=1.6.1',
'Flask>=0.10.1',
'Flask-Login>=0.2.9',
'Flask-Mail>=0.9.0',
'Flask-Principal>=0.4.0',
'Flask-WTF>=0.9.3',
'passlib>=1.6.2',
],
test_suite='nose.collector',
tests_require=[
@@ -48,7 +47,7 @@ setup(
'Flask-SQLAlchemy',
'Flask-MongoEngine',
'Flask-Peewee',
'py-bcrypt',
'bcrypt',
'simplejson'
],
classifiers=[
+1 -1
View File
@@ -61,7 +61,7 @@ class SecurityTest(TestCase):
return self._get(endpoint or '/logout', follow_redirects=True)
def assertIsHomePage(self, data):
self.assertIn('Home Page', data)
self.assertIn(b'Home Page', data)
def assertIn(self, member, container, msg=None):
if hasattr(TestCase, 'assertIn'):
+100 -84
View File
@@ -1,17 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import with_statement
# from __future__ import with_statement
import base64
import time
import simplejson as json
import flask
from flask.ext.security.utils import capture_registrations, \
from flask_security.utils import capture_registrations, \
capture_reset_password_requests, capture_passwordless_login_requests
from flask.ext.security.forms import LoginForm, ConfirmRegisterForm, RegisterForm, \
from flask_security.forms import LoginForm, ConfirmRegisterForm, RegisterForm, \
ForgotPasswordForm, ResetPasswordForm, SendConfirmationForm, \
PasswordlessLoginForm
from flask.ext.security.forms import TextField, SubmitField, valid_user_email
from flask_security.forms import TextField, SubmitField, valid_user_email
from flask_security.signals import user_registered
from tests import SecurityTest
@@ -27,7 +30,7 @@ class ConfiguredPasswordHashSecurityTests(SecurityTest):
def test_authenticate(self):
r = self.authenticate(endpoint="/login")
self.assertIn('Home Page', r.data)
self.assertIn(b'Home Page', r.data)
class ConfiguredSecurityTests(SecurityTest):
@@ -45,20 +48,20 @@ class ConfiguredSecurityTests(SecurityTest):
def test_login_view(self):
r = self._get('/custom_login')
self.assertIn("<h1>Login</h1>", r.data)
self.assertIn(b"<h1>Login</h1>", r.data)
def test_authenticate(self):
r = self.authenticate(endpoint="/custom_login")
self.assertIn('Post Login', r.data)
self.assertIn(b'Post Login', r.data)
def test_logout(self):
self.authenticate(endpoint="/custom_login")
r = self.logout(endpoint="/custom_logout")
self.assertIn('Post Logout', r.data)
self.assertIn(b'Post Logout', r.data)
def test_register_view(self):
r = self._get('/register')
self.assertIn('<h1>Register</h1>', r.data)
self.assertIn(b'<h1>Register</h1>', r.data)
def test_register(self):
data = dict(email='dude@lp.com',
@@ -66,7 +69,7 @@ class ConfiguredSecurityTests(SecurityTest):
password_confirm='password')
r = self._post('/register', data=data, follow_redirects=True)
self.assertIn('Post Register', r.data)
self.assertIn(b'Post Register', r.data)
def test_register_with_next_querystring_argument(self):
data = dict(email='dude@lp.com',
@@ -74,7 +77,7 @@ class ConfiguredSecurityTests(SecurityTest):
password_confirm='password')
r = self._post('/register?next=/page1', data=data, follow_redirects=True)
self.assertIn('Page 1', r.data)
self.assertIn(b'Page 1', r.data)
def test_register_json(self):
data = '{ "email": "dude@lp.com", "password": "password"}'
@@ -87,20 +90,20 @@ class ConfiguredSecurityTests(SecurityTest):
password='password',
password_confirm='password')
r = self._post('/register', data=data, follow_redirects=True)
msg = 'matt@lp.com is already associated with an account'
msg = b'matt@lp.com is already associated with an account'
self.assertIn(msg, r.data)
def test_unauthorized(self):
self.authenticate("joe@lp.com", endpoint="/custom_auth")
r = self._get("/admin", follow_redirects=True)
msg = 'You are not allowed to access the requested resouce'
msg = b'You are not allowed to access the requested resouce'
self.assertIn(msg, r.data)
def test_default_http_auth_realm(self):
r = self._get('/http', headers={
'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:bogus")
'Authorization': 'Basic %s' % base64.b64encode(b"joe@lp.com:bogus")
})
self.assertIn('<h1>Unauthorized</h1>', r.data)
self.assertIn(b'<h1>Unauthorized</h1>', r.data)
self.assertIn('WWW-Authenticate', r.headers)
self.assertEquals('Basic realm="Custom Realm"',
r.headers['WWW-Authenticate'])
@@ -125,7 +128,7 @@ class DefaultTemplatePathTests(SecurityTest):
def test_login_user_template(self):
r = self._get('/login')
self.assertIn('CUSTOM LOGIN USER', r.data)
self.assertIn(b'CUSTOM LOGIN USER', r.data)
class RegisterableTemplatePathTests(SecurityTest):
@@ -137,7 +140,7 @@ class RegisterableTemplatePathTests(SecurityTest):
def test_register_user_template(self):
r = self._get('/register')
self.assertIn('CUSTOM REGISTER USER', r.data)
self.assertIn(b'CUSTOM REGISTER USER', r.data)
class RecoverableTemplatePathTests(SecurityTest):
@@ -150,7 +153,7 @@ class RecoverableTemplatePathTests(SecurityTest):
def test_forgot_password_template(self):
r = self._get('/reset')
self.assertIn('CUSTOM FORGOT PASSWORD', r.data)
self.assertIn(b'CUSTOM FORGOT PASSWORD', r.data)
def test_reset_password_template(self):
with capture_reset_password_requests() as requests:
@@ -161,7 +164,7 @@ class RecoverableTemplatePathTests(SecurityTest):
r = self._get('/reset/' + t)
self.assertIn('CUSTOM RESET PASSWORD', r.data)
self.assertIn(b'CUSTOM RESET PASSWORD', r.data)
class ConfirmableTemplatePathTests(SecurityTest):
@@ -173,7 +176,7 @@ class ConfirmableTemplatePathTests(SecurityTest):
def test_send_confirmation_template(self):
r = self._get('/confirm')
self.assertIn('CUSTOM SEND CONFIRMATION', r.data)
self.assertIn(b'CUSTOM SEND CONFIRMATION', r.data)
class PasswordlessTemplatePathTests(SecurityTest):
@@ -185,7 +188,7 @@ class PasswordlessTemplatePathTests(SecurityTest):
def test_send_login_template(self):
r = self._get('/login')
self.assertIn('CUSTOM SEND LOGIN', r.data)
self.assertIn(b'CUSTOM SEND LOGIN', r.data)
class RegisterableTests(SecurityTest):
@@ -200,7 +203,7 @@ class RegisterableTests(SecurityTest):
password_confirm='password')
self._post('/register', data=data, follow_redirects=True)
r = self.authenticate('dude@lp.com')
self.assertIn('Hello dude@lp.com', r.data)
self.assertIn(b'Hello dude@lp.com', r.data)
class ConfirmableTests(SecurityTest):
@@ -215,19 +218,20 @@ class ConfirmableTests(SecurityTest):
e = 'dude@lp.com'
self.register(e)
r = self.authenticate(email=e)
self.assertIn(self.get_message('CONFIRMATION_REQUIRED'), r.data)
self.assertIn(self.get_message('CONFIRMATION_REQUIRED').encode('utf-8'), r.data)
def test_send_confirmation_of_already_confirmed_account(self):
e = 'dude@lp.com'
with capture_registrations() as registrations:
self.register(e)
r = self.register(e)
token = registrations[0]['confirm_token']
self.client.get('/confirm/' + token, follow_redirects=True)
self.logout()
r = self._post('/confirm', data=dict(email=e))
self.assertIn(self.get_message('ALREADY_CONFIRMED'), r.data)
m = self.get_message('ALREADY_CONFIRMED')
self.assertIn(m.encode('utf-8'), r.data)
def test_register_sends_confirmation_email(self):
e = 'dude@lp.com'
@@ -240,19 +244,22 @@ class ConfirmableTests(SecurityTest):
def test_confirm_email(self):
e = 'dude@lp.com'
with capture_registrations() as registrations:
self.register(e)
token = registrations[0]['confirm_token']
tokens = []
def on_registered(sender, **kwargs):
tokens.append(kwargs['confirm_token'])
r = self.client.get('/confirm/' + token, follow_redirects=True)
user_registered.connect(on_registered, self.app)
r = self.register(e)
self.assertEqual(len(tokens), 1)
r = self.client.get('/confirm/' + tokens[0], follow_redirects=True)
msg = self.app.config['SECURITY_MSG_EMAIL_CONFIRMED'][0]
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
def test_invalid_token_when_confirming_email(self):
r = self.client.get('/confirm/bogus', follow_redirects=True)
msg = self.app.config['SECURITY_MSG_INVALID_CONFIRMATION_TOKEN'][0]
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
def test_send_confirmation_json(self):
r = self._post('/confirm', data='{"email": "matt@lp.com"}',
@@ -262,14 +269,14 @@ class ConfirmableTests(SecurityTest):
def test_send_confirmation_with_invalid_email(self):
r = self._post('/confirm', data=dict(email='bogus@bogus.com'))
msg = self.app.config['SECURITY_MSG_USER_DOES_NOT_EXIST'][0]
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
def test_resend_confirmation(self):
e = 'dude@lp.com'
self.register(e)
r = self._post('/confirm', data={'email': e})
msg = self.get_message('CONFIRMATION_REQUEST', email=e)
msg = self.get_message('CONFIRMATION_REQUEST', email=e).encode('utf-8')
self.assertIn(msg, r.data)
def test_user_deleted_before_confirmation(self):
@@ -287,7 +294,7 @@ class ConfirmableTests(SecurityTest):
r = self.client.get('/confirm/' + token, follow_redirects=True)
msg = self.app.config['SECURITY_MSG_INVALID_CONFIRMATION_TOKEN'][0]
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
class ExpiredConfirmationTest(SecurityTest):
@@ -316,7 +323,7 @@ class ExpiredConfirmationTest(SecurityTest):
expire_text = self.AUTH_CONFIG['SECURITY_CONFIRM_EMAIL_WITHIN']
msg = self.app.config['SECURITY_MSG_CONFIRMATION_EXPIRED'][0]
msg = msg % dict(within=expire_text, email=e)
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
class LoginWithoutImmediateConfirmTests(SecurityTest):
@@ -332,7 +339,7 @@ class LoginWithoutImmediateConfirmTests(SecurityTest):
p = 'password'
data = dict(email=e, password=p, password_confirm=p)
r = self._post('/register', data=data, follow_redirects=True)
self.assertIn(e, r.data)
self.assertIn(e.encode('utf-8'), r.data)
def test_confirm_email_of_user_different_than_current_user(self):
e1 = 'dude@lp.com'
@@ -340,6 +347,7 @@ class LoginWithoutImmediateConfirmTests(SecurityTest):
with capture_registrations() as registrations:
self.register(e1)
self.logout()
self.register(e2)
token1 = registrations[0]['confirm_token']
token2 = registrations[1]['confirm_token']
@@ -348,19 +356,19 @@ class LoginWithoutImmediateConfirmTests(SecurityTest):
self.client.get('/logout')
self.authenticate(email=e1)
r = self.client.get('/confirm/' + token2, follow_redirects=True)
msg = self.app.config['SECURITY_MSG_EMAIL_CONFIRMED'][0]
self.assertIn(msg, r.data)
self.assertIn('Hello %s' % e2, r.data)
m = self.app.config['SECURITY_MSG_EMAIL_CONFIRMED'][0]
self.assertIn(m.encode('utf-8'), r.data)
self.assertIn(b'Hello lady@lp.com', r.data)
def test_login_unconfirmed_user_when_login_without_confirmation_is_true(self):
e = 'dude@lp.com'
p = 'password'
data = dict(email=e, password=p, password_confirm=p)
r = self._post('/register', data=data, follow_redirects=True)
self.assertIn(e, r.data)
self.assertIn(e.encode('utf-8'), r.data)
self.client.get('/logout')
r = self.authenticate(email=e)
self.assertIn(e, r.data)
self.assertIn(e.encode('utf-8'), r.data)
class RecoverableTests(SecurityTest):
@@ -377,7 +385,7 @@ class RecoverableTests(SecurityTest):
follow_redirects=True)
t = requests[0]['token']
r = self._get('/reset/' + t)
self.assertIn('<h1>Reset password</h1>', r.data)
self.assertIn(b'<h1>Reset password</h1>', r.data)
def test_forgot_post_sends_email(self):
with capture_reset_password_requests():
@@ -393,7 +401,7 @@ class RecoverableTests(SecurityTest):
def test_forgot_password_invalid_email(self):
r = self._post('/reset', data=dict(email='larry@lp.com'),
follow_redirects=True)
self.assertIn("Specified user does not exist", r.data)
self.assertIn(b"Specified user does not exist", r.data)
def test_reset_password_with_valid_token(self):
with capture_reset_password_requests() as requests:
@@ -408,15 +416,25 @@ class RecoverableTests(SecurityTest):
r = self.logout()
r = self.authenticate('joe@lp.com', 'newpassword')
self.assertIn('Hello joe@lp.com', r.data)
self.assertIn(b'Hello joe@lp.com', r.data)
def test_reset_password_with_invalid_token(self):
r = self._post('/reset/bogus', data={
'password': 'newpassword',
'password_confirm': 'newpassword'
}, follow_redirects=True)
m = self.get_message('INVALID_RESET_PASSWORD_TOKEN')
self.assertIn(m.encode('utf-8'), r.data)
self.assertIn(self.get_message('INVALID_RESET_PASSWORD_TOKEN'), r.data)
def test_reset_password_with_mangled_token(self):
t = "WyIxNjQ2MzYiLCIxMzQ1YzBlZmVhM2VhZjYwODgwMDhhZGU2YzU0MzZjMiJd.BZEw_Q.lQyo3npdPZtcJ_sNHVHP103syjM&url_id=fbb89a8328e58c181ea7d064c2987874bc54a23d"
r = self._post('/reset/' + t, data={
'password': 'newpassword',
'password_confirm': 'newpassword'
}, follow_redirects=True)
m = self.get_message('INVALID_RESET_PASSWORD_TOKEN')
self.assertIn(m.encode('utf-8'), r.data)
class ExpiredResetPasswordTest(SecurityTest):
@@ -439,7 +457,7 @@ class ExpiredResetPasswordTest(SecurityTest):
'password_confirm': 'newpassword'
}, follow_redirects=True)
self.assertIn('You did not reset your password within', r.data)
self.assertIn(b'You did not reset your password within', r.data)
class ChangePasswordTest(SecurityTest):
@@ -452,7 +470,7 @@ class ChangePasswordTest(SecurityTest):
def test_change_password(self):
self.authenticate()
r = self.client.get('/change', follow_redirects=True)
self.assertIn('Change password', r.data)
self.assertIn(b'Change password', r.data)
def test_change_password_invalid(self):
self.authenticate()
@@ -461,8 +479,8 @@ class ChangePasswordTest(SecurityTest):
'new_password': 'newpassword',
'new_password_confirm': 'newpassword'
}, follow_redirects=True)
self.assertNotIn('You successfully changed your password', r.data)
self.assertIn('Invalid password', r.data)
self.assertNotIn(b'You successfully changed your password', r.data)
self.assertIn(b'Invalid password', r.data)
def test_change_password_mismatch(self):
self.authenticate()
@@ -471,8 +489,8 @@ class ChangePasswordTest(SecurityTest):
'new_password': 'newpassword',
'new_password_confirm': 'notnewpassword'
}, follow_redirects=True)
self.assertNotIn('You successfully changed your password', r.data)
self.assertIn('Passwords do not match', r.data)
self.assertNotIn(b'You successfully changed your password', r.data)
self.assertIn(b'Passwords do not match', r.data)
def test_change_password_bad_password(self):
self.authenticate()
@@ -481,8 +499,8 @@ class ChangePasswordTest(SecurityTest):
'new_password': 'a',
'new_password_confirm': 'a'
}, follow_redirects=True)
self.assertNotIn('You successfully changed your password', r.data)
self.assertIn('Password must be at least 6 characters', r.data)
self.assertNotIn(b'You successfully changed your password', r.data)
self.assertIn(b'Password must be at least 6 characters', r.data)
def test_change_password_same_as_previous(self):
self.authenticate()
@@ -491,8 +509,8 @@ class ChangePasswordTest(SecurityTest):
'new_password': 'password',
'new_password_confirm': 'password'
}, follow_redirects=True)
self.assertNotIn('You successfully changed your password', r.data)
self.assertIn('Your new password must be different than your previous password.', r.data)
self.assertNotIn(b'You successfully changed your password', r.data)
self.assertIn(b'Your new password must be different than your previous password.', r.data)
def test_change_password_success(self):
data = {
@@ -505,8 +523,8 @@ class ChangePasswordTest(SecurityTest):
with self.app.extensions['mail'].record_messages() as outbox:
r = self._post('/change', data=data, follow_redirects=True)
self.assertIn('You successfully changed your password', r.data)
self.assertIn('Home Page', r.data)
self.assertIn(b'You successfully changed your password', r.data)
self.assertIn(b'Home Page', r.data)
self.assertEqual(len(outbox), 1)
self.assertIn("Your password has been changed", outbox[0].html)
@@ -531,8 +549,7 @@ class EmailConfigTest(SecurityTest):
self.authenticate()
with self.app.extensions['mail'].record_messages() as outbox:
r = self._post('/change', data=data, follow_redirects=True)
self._post('/change', data=data, follow_redirects=True)
self.assertEqual(len(outbox), 0)
@@ -552,7 +569,7 @@ class ChangePasswordPostViewTest(SecurityTest):
self.authenticate()
r = self._post('/change', data=data, follow_redirects=True)
self.assertIn('Profile Page', r.data)
self.assertIn(b'Profile Page', r.data)
class ChangePasswordDisabledTest(SecurityTest):
@@ -599,18 +616,18 @@ class PasswordlessTests(SecurityTest):
msg = self.app.config['SECURITY_MSG_DISABLED_ACCOUNT'][0]
r = self._post('/login', data=dict(email='tiya@lp.com'),
follow_redirects=True)
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
def test_request_login_token_with_json_and_valid_email(self):
data = '{"email": "matt@lp.com", "password": "password"}'
r = self._post('/login', data=data, content_type='application/json')
self.assertEquals(r.status_code, 200)
self.assertNotIn('error', r.data)
self.assertNotIn(b'error', r.data)
def test_request_login_token_with_json_and_invalid_email(self):
data = '{"email": "nobody@lp.com", "password": "password"}'
r = self._post('/login', data=data, content_type='application/json')
self.assertIn('errors', r.data)
self.assertIn(b'errors', r.data)
def test_request_login_token_sends_email_and_can_login(self):
e = 'matt@lp.com'
@@ -632,19 +649,19 @@ class PasswordlessTests(SecurityTest):
msg = self.app.config['SECURITY_MSG_LOGIN_EMAIL_SENT'][0]
msg = msg % dict(email=user.email)
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
r = self.client.get('/login/' + token, follow_redirects=True)
msg = self.get_message('PASSWORDLESS_LOGIN_SUCCESSFUL')
msg = self.get_message('PASSWORDLESS_LOGIN_SUCCESSFUL').encode('utf-8')
self.assertIn(msg, r.data)
r = self.client.get('/profile')
self.assertIn('Profile Page', r.data)
self.assertIn(b'Profile Page', r.data)
def test_invalid_login_token(self):
msg = self.app.config['SECURITY_MSG_INVALID_LOGIN_TOKEN'][0]
m = self.app.config['SECURITY_MSG_INVALID_LOGIN_TOKEN'][0]
r = self._get('/login/bogus', follow_redirects=True)
self.assertIn(msg, r.data)
self.assertIn(m.encode('utf-8'), r.data)
def test_token_login_when_already_authenticated(self):
with capture_passwordless_login_requests() as requests:
@@ -654,15 +671,15 @@ class PasswordlessTests(SecurityTest):
r = self.client.get('/login/' + token, follow_redirects=True)
msg = self.get_message('PASSWORDLESS_LOGIN_SUCCESSFUL')
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
r = self.client.get('/login/' + token, follow_redirects=True)
msg = self.get_message('PASSWORDLESS_LOGIN_SUCCESSFUL')
self.assertNotIn(msg, r.data)
self.assertNotIn(msg.encode('utf-8'), r.data)
def test_send_login_with_invalid_email(self):
r = self._post('/login', data=dict(email='bogus@bogus.com'))
self.assertIn('Specified user does not exist', r.data)
self.assertIn(b'Specified user does not exist', r.data)
class ExpiredLoginTokenTests(SecurityTest):
@@ -688,8 +705,7 @@ class ExpiredLoginTokenTests(SecurityTest):
expire_text = self.AUTH_CONFIG['SECURITY_LOGIN_WITHIN']
msg = self.app.config['SECURITY_MSG_LOGIN_EXPIRED'][0]
msg = msg % dict(within=expire_text, email=e)
self.assertIn(msg, r.data)
self.assertIn(msg.encode('utf-8'), r.data)
self.assertEqual(len(outbox), 1)
self.assertIn(e, outbox[0].html)
self.assertNotIn(token, outbox[0].html)
@@ -730,9 +746,9 @@ class NoBlueprintTests(SecurityTest):
self.assertEqual(404, r.status_code)
def test_http_auth_without_blueprint(self):
auth = 'Basic ' + base64.b64encode("matt@lp.com:password")
r = self._get('/http', headers={'Authorization': auth})
self.assertIn('HTTP Authentication', r.data)
auth = base64.b64encode(b"matt@lp.com:password").decode('utf-8')
r = self._get('/http', headers={'Authorization': 'basic %s' % auth})
self.assertIn(b'HTTP Authentication', r.data)
class ExtendFormsTest(SecurityTest):
@@ -755,11 +771,11 @@ class ExtendFormsTest(SecurityTest):
def test_login_view(self):
r = self._get('/login', follow_redirects=True)
self.assertIn("My Login Email Address Field", r.data)
self.assertIn(b"My Login Email Address Field", r.data)
def test_register(self):
r = self._get('/register', follow_redirects=True)
self.assertIn("My Register Email Address Field", r.data)
self.assertIn(b"My Register Email Address Field", r.data)
class RecoverableExtendFormsTest(SecurityTest):
@@ -782,7 +798,7 @@ class RecoverableExtendFormsTest(SecurityTest):
def test_forgot_password(self):
r = self._get('/reset', follow_redirects=True)
self.assertIn("My Forgot Password Email Address Field", r.data)
self.assertIn(b"My Forgot Password Email Address Field", r.data)
def test_reset_password(self):
with capture_reset_password_requests() as requests:
@@ -790,7 +806,7 @@ class RecoverableExtendFormsTest(SecurityTest):
follow_redirects=True)
token = requests[0]['token']
r = self._get('/reset/' + token)
self.assertIn("My Reset Password Submit Field", r.data)
self.assertIn(b"My Reset Password Submit Field", r.data)
class PasswordlessExtendFormsTest(SecurityTest):
@@ -808,7 +824,7 @@ class PasswordlessExtendFormsTest(SecurityTest):
def test_passwordless_login(self):
r = self._get('/login', follow_redirects=True)
self.assertIn("My Passwordless Login Email Address Field", r.data)
self.assertIn(b"My Passwordless Login Email Address Field", r.data)
class ConfirmableExtendFormsTest(SecurityTest):
@@ -831,11 +847,11 @@ class ConfirmableExtendFormsTest(SecurityTest):
def test_register(self):
r = self._get('/register', follow_redirects=True)
self.assertIn("My Confirm Register Email Address Field", r.data)
self.assertIn(b"My Confirm Register Email Address Field", r.data)
def test_send_confirmation(self):
r = self._get('/confirm', follow_redirects=True)
self.assertIn("My Send Confirmation Email Address Field", r.data)
self.assertIn(b"My Send Confirmation Email Address Field", r.data)
class AdditionalUserIdentityAttributes(SecurityTest):
@@ -846,4 +862,4 @@ class AdditionalUserIdentityAttributes(SecurityTest):
def test_authenticate(self):
r = self.authenticate(email='matt')
self.assertIn('Hello matt@lp.com', r.data)
self.assertIn(b'Hello matt@lp.com', r.data)
+43 -39
View File
@@ -4,7 +4,11 @@ from __future__ import with_statement
import base64
import simplejson as json
from cookielib import Cookie
try:
from cookielib import Cookie
except ImportError:
from http.cookiejar import Cookie
from werkzeug.utils import parse_cookie
@@ -27,35 +31,35 @@ class DefaultSecurityTests(SecurityTest):
def test_login_view(self):
r = self._get('/login')
self.assertIn('<h1>Login</h1>', r.data)
self.assertIn(b'<h1>Login</h1>', r.data)
def test_authenticate(self):
r = self.authenticate()
self.assertIn('Hello matt@lp.com', r.data)
self.assertIn(b'Hello matt@lp.com', r.data)
def test_authenticate_case_insensitive_email(self):
r = self.authenticate(email='MATT@lp.com')
self.assertIn('Hello matt@lp.com', r.data)
self.assertIn(b'Hello matt@lp.com', r.data)
def test_unprovided_username(self):
r = self.authenticate("")
self.assertIn(self.get_message('EMAIL_NOT_PROVIDED'), r.data)
self.assertIn(self.get_message('EMAIL_NOT_PROVIDED').encode('utf-8'), r.data)
def test_unprovided_password(self):
r = self.authenticate(password="")
self.assertIn(self.get_message('PASSWORD_NOT_PROVIDED'), r.data)
self.assertIn(self.get_message('PASSWORD_NOT_PROVIDED').encode('utf-8'), r.data)
def test_invalid_user(self):
r = self.authenticate(email="bogus@bogus.com")
self.assertIn(self.get_message('USER_DOES_NOT_EXIST'), r.data)
self.assertIn(self.get_message('USER_DOES_NOT_EXIST').encode('utf-8'), r.data)
def test_bad_password(self):
r = self.authenticate(password="bogus")
self.assertIn(self.get_message('INVALID_PASSWORD'), r.data)
self.assertIn(self.get_message('INVALID_PASSWORD').encode('utf-8'), r.data)
def test_inactive_user(self):
r = self.authenticate("tiya@lp.com", "password")
self.assertIn(self.get_message('DISABLED_ACCOUNT'), r.data)
self.assertIn(self.get_message('DISABLED_ACCOUNT').encode('utf-8'), r.data)
def test_logout(self):
self.authenticate()
@@ -65,17 +69,17 @@ class DefaultSecurityTests(SecurityTest):
def test_unauthorized_access(self):
self.logout()
r = self._get('/profile', follow_redirects=True)
self.assertIn('<li class="info">Please log in to access this page.</li>', r.data)
self.assertIn(b'<li class="info">Please log in to access this page.</li>', r.data)
def test_authorized_access(self):
self.authenticate()
r = self._get("/profile")
self.assertIn('profile', r.data)
self.assertIn(b'profile', r.data)
def test_valid_admin_role(self):
self.authenticate()
r = self._get("/admin")
self.assertIn('Admin Page', r.data)
self.assertIn(b'Admin Page', r.data)
def test_invalid_admin_role(self):
self.authenticate("joe@lp.com")
@@ -86,7 +90,7 @@ class DefaultSecurityTests(SecurityTest):
for user in ("matt@lp.com", "joe@lp.com"):
self.authenticate(user)
r = self._get("/admin_or_editor")
self.assertIn('Admin or Editor Page', r.data)
self.assertIn(b'Admin or Editor Page', r.data)
self.logout()
self.authenticate("jill@lp.com")
@@ -95,7 +99,7 @@ class DefaultSecurityTests(SecurityTest):
def test_unauthenticated_role_required(self):
r = self._get('/admin', follow_redirects=True)
self.assertIn(self.get_message('UNAUTHORIZED'), r.data)
self.assertIn(self.get_message('UNAUTHORIZED').encode('utf-8'), r.data)
def test_multiple_role_required(self):
for user in ("matt@lp.com", "joe@lp.com"):
@@ -106,7 +110,7 @@ class DefaultSecurityTests(SecurityTest):
self.authenticate('dave@lp.com')
r = self._get("/admin_and_editor", follow_redirects=True)
self.assertIn('Admin and Editor Page', r.data)
self.assertIn(b'Admin and Editor Page', r.data)
def test_ok_json_auth(self):
r = self.json_authenticate()
@@ -116,14 +120,14 @@ class DefaultSecurityTests(SecurityTest):
def test_invalid_json_auth(self):
r = self.json_authenticate(password='junk')
self.assertIn('"code": 400', r.data)
self.assertIn(b'"code": 400', r.data)
def test_token_auth_via_querystring_valid_token(self):
r = self.json_authenticate()
data = json.loads(r.data)
token = data['response']['user']['authentication_token']
r = self._get('/token?auth_token=' + token)
self.assertIn('Token Authentication', r.data)
self.assertIn(b'Token Authentication', r.data)
def test_token_auth_via_header_valid_token(self):
r = self.json_authenticate()
@@ -131,7 +135,7 @@ class DefaultSecurityTests(SecurityTest):
token = data['response']['user']['authentication_token']
headers = {"Authentication-Token": token}
r = self._get('/token', headers=headers)
self.assertIn('Token Authentication', r.data)
self.assertIn(b'Token Authentication', r.data)
def test_token_auth_via_querystring_invalid_token(self):
r = self._get('/token?auth_token=X')
@@ -143,61 +147,61 @@ class DefaultSecurityTests(SecurityTest):
def test_http_auth(self):
r = self._get('/http', headers={
'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:password")
'Authorization': 'Basic %s' % base64.b64encode(b"joe@lp.com:password").decode('utf-8')
})
self.assertIn('HTTP Authentication', r.data)
self.assertIn(b'HTTP Authentication', r.data)
def test_http_auth_no_authorization(self):
r = self._get('/http', headers={})
self.assertIn('<h1>Unauthorized</h1>', r.data)
self.assertIn(b'<h1>Unauthorized</h1>', r.data)
self.assertIn('WWW-Authenticate', r.headers)
self.assertEquals('Basic realm="Login Required"',
r.headers['WWW-Authenticate'])
def test_invalid_http_auth_invalid_username(self):
r = self._get('/http', headers={
'Authorization': 'Basic ' + base64.b64encode("bogus:bogus")
'Authorization': 'Basic %s' % base64.b64encode(b"bogus:bogus").decode('utf-8')
})
self.assertIn('<h1>Unauthorized</h1>', r.data)
self.assertIn(b'<h1>Unauthorized</h1>', r.data)
self.assertIn('WWW-Authenticate', r.headers)
self.assertEquals('Basic realm="Login Required"',
r.headers['WWW-Authenticate'])
def test_invalid_http_auth_bad_password(self):
r = self._get('/http', headers={
'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:bogus")
'Authorization': 'Basic %s' % base64.b64encode(b"joe@lp.com:bogus").decode('utf-8')
})
self.assertIn('<h1>Unauthorized</h1>', r.data)
self.assertIn(b'<h1>Unauthorized</h1>', r.data)
self.assertIn('WWW-Authenticate', r.headers)
self.assertEquals('Basic realm="Login Required"',
r.headers['WWW-Authenticate'])
def test_custom_http_auth_realm(self):
r = self._get('/http_custom_realm', headers={
'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:bogus")
'Authorization': 'Basic %s' % base64.b64encode(b"joe@lp.com:bogus").decode('utf-8')
})
self.assertIn('<h1>Unauthorized</h1>', r.data)
self.assertIn(b'<h1>Unauthorized</h1>', r.data)
self.assertIn('WWW-Authenticate', r.headers)
self.assertEquals('Basic realm="My Realm"',
r.headers['WWW-Authenticate'])
def test_multi_auth_basic(self):
r = self._get('/multi_auth', headers={
'Authorization': 'Basic ' + base64.b64encode("joe@lp.com:password")
'Authorization': 'Basic %s' % base64.b64encode(b"joe@lp.com:password").decode('utf-8')
})
self.assertIn('Basic', r.data)
self.assertIn(b'Basic', r.data)
def test_multi_auth_token(self):
r = self.json_authenticate()
data = json.loads(r.data)
token = data['response']['user']['authentication_token']
r = self._get('/multi_auth?auth_token=' + token)
self.assertIn('Token', r.data)
self.assertIn(b'Token', r.data)
def test_multi_auth_session(self):
self.authenticate()
r = self._get('/multi_auth')
self.assertIn('Session', r.data)
self.assertIn(b'Session', r.data)
def test_user_deleted_during_session_reverts_to_anonymous_user(self):
self.authenticate()
@@ -208,13 +212,13 @@ class DefaultSecurityTests(SecurityTest):
self.app.security.datastore.commit()
r = self._get('/')
self.assertNotIn('Hello matt@lp.com', r.data)
self.assertNotIn(b'Hello matt@lp.com', r.data)
def test_remember_token(self):
r = self.authenticate(follow_redirects=False)
self.client.cookie_jar.clear_session_cookies()
r = self._get('/profile')
self.assertIn('profile', r.data)
self.assertIn(b'profile', r.data)
def test_token_loader_does_not_fail_with_invalid_token(self):
c = Cookie(version=0, name='remember_token', value='None', port=None,
@@ -226,7 +230,7 @@ class DefaultSecurityTests(SecurityTest):
self.client.cookie_jar.set_cookie(c)
r = self._get('/')
self.assertNotIn('BadSignature', r.data)
self.assertNotIn(b'BadSignature', r.data)
class MongoEngineSecurityTests(DefaultSecurityTests):
@@ -247,23 +251,23 @@ class DefaultDatastoreTests(SecurityTest):
def test_add_role_to_user(self):
r = self._get('/coverage/add_role_to_user')
self.assertIn('success', r.data)
self.assertIn(b'success', r.data)
def test_remove_role_from_user(self):
r = self._get('/coverage/remove_role_from_user')
self.assertIn('success', r.data)
self.assertIn(b'success', r.data)
def test_activate_user(self):
r = self._get('/coverage/activate_user')
self.assertIn('success', r.data)
self.assertIn(b'success', r.data)
def test_deactivate_user(self):
r = self._get('/coverage/deactivate_user')
self.assertIn('success', r.data)
self.assertIn(b'success', r.data)
def test_invalid_role(self):
r = self._get('/coverage/invalid_role')
self.assertIn('success', r.data)
self.assertIn(b'success', r.data)
class MongoEngineDatastoreTests(DefaultDatastoreTests):
+7 -3
View File
@@ -137,9 +137,13 @@ def create_users(count=None):
for u in users[:count]:
pw = encrypt_password(u[2])
ds.create_user(email=u[0], username=u[1], password=pw,
roles=u[3], active=u[4])
ds.commit()
roles = [ds.find_or_create_role(rn) for rn in u[3]]
ds.commit()
user = ds.create_user(email=u[0], username=u[1], password=pw, active=u[4])
ds.commit()
for role in roles:
ds.add_role_to_user(user, role)
ds.commit()
def populate_data(user_count=None):
+3 -3
View File
@@ -1,5 +1,5 @@
[tox]
envlist = py26, py27
envlist = py26, py27, py33, pypy
[testenv]
deps =
@@ -8,6 +8,6 @@ deps =
Flask-SQLAlchemy
Flask-MongoEngine
Flask-Peewee
py-bcrypt
bcrypt
commands = nosetests []
commands = nosetests -xs []