mirror of
https://github.com/wassname/flask-security.git
synced 2026-06-27 16:10:11 +08:00
Merge https://github.com/mattupstate/flask-security into develop
This commit is contained in:
@@ -31,3 +31,5 @@ env/
|
||||
|
||||
#Editor temporaries
|
||||
*~
|
||||
|
||||
*.db
|
||||
|
||||
+3
-4
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
import blinker
|
||||
|
||||
|
||||
signals = blinker.Namespace()
|
||||
|
||||
user_registered = signals.signal("user-registered")
|
||||
|
||||
+28
-11
@@ -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():
|
||||
|
||||
@@ -98,6 +98,7 @@ def logout():
|
||||
get_url(_security.post_logout_view))
|
||||
|
||||
|
||||
@anonymous_user_required
|
||||
def register():
|
||||
"""View function which handles a registration request."""
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user