Compare commits

..
36 Commits
Author SHA1 Message Date
Matt Wright 0f46f35981 Bump version number to 1.6.7 2013-07-11 14:58:46 -04:00
Matt Wright c0d9eecf10 Set release date. 2013-07-11 14:58:36 -04:00
Matt Wright 04bb2c4041 Update CHANGES 2013-07-11 14:54:22 -04:00
Matt Wright 8eeb832d2e Conditionally logout the current user when confirming an email address to prevent unnecessary code/signals to be fired. Fixes #133 2013-07-11 14:50:21 -04:00
Matt Wright 8f760aadbd Merge pull request #134 from rxl/email_confirmation_fix
Email confirmation fix
2013-07-11 11:21:05 -07:00
rxl 03d27cd600 add logout_user() to the beginning of confirm_email() 2013-07-11 14:14:50 -04:00
rxl d30a27b3bb remove '@anonymous_user_required' from confirm_email() 2013-07-11 14:14:28 -04:00
Matt Wright 78903fa2e5 Make password length message configurable. 2013-07-02 10:36:22 -04:00
Matt Wright 514de64303 Bump version number to 1.6.6 2013-06-28 17:24:50 -04:00
Matt Wright bad63265f8 Update CHANGES 2013-06-28 17:24:40 -04:00
Matt Wright 66a9dcd2e6 Merge branch 'develop' of github.com:mattupstate/flask-security into develop 2013-06-28 17:23:36 -04:00
Matt Wright df1647f1f9 Fix install_requires in setup.py 2013-06-28 17:23:16 -04:00
Matt Wright 89ecded480 Merge pull request #128 from vkotovv/develop
Fix issue tracker link
2013-06-26 08:04:10 -07:00
Vadim Kotov 2b35b37a66 Update sidebarintro.html
Fixed link to the issue tracker
2013-06-26 18:30:44 +04:00
Matt Wright 4d8a813004 Bump version number to 1.6.5 2013-06-20 16:01:36 -04:00
Matt Wright 06312ef50f Fix typo with _external parameter in confirmable.py. Fixes #126 2013-06-20 16:01:22 -04:00
Matt Wright c3ad5b2fa6 Take --use-mirrors out of pip calls in .travis.yml 2013-06-18 15:53:32 -04:00
Matt Wright 637fc913cd Take Flask-Mail out of .travis.yml 2013-06-18 15:32:32 -04:00
Matt Wright e00522f331 Add SECURITY_CHANGEABLE to docs. Fixes #115 2013-06-18 15:05:32 -04:00
Matt Wright 3d34d87a97 Bump version number to 1.6.4 2013-06-18 15:00:41 -04:00
Matt Wright 461ace9303 Update docs 2013-06-18 15:00:34 -04:00
Matt Wright d19bb98abd Version 1.6.4 changes. Refer to CHANGES for updates. Fixes #123 #121 #120 $119 2013-06-18 14:56:12 -04:00
Matt Wright c24af5ca6e Whitespace! 2013-05-28 11:11:37 -04:00
Matt Wright 26045fc4dc Use the _external parameter when generating links for emails 2013-05-28 11:11:19 -04:00
Matt Wright bf260d4b7e Add optional next parameter to registration endpoint. Fixes #117. 2013-05-28 11:01:42 -04:00
Matt Wright db56ff74a9 Bump version number to 1.6.3 2013-05-08 12:29:48 -04:00
Matt Wright e03efe0b34 Update CHANGES 2013-05-08 12:29:35 -04:00
Matt Wright c587988a3a Merge branch 'develop' of github.com:mattupstate/flask-security into develop 2013-05-03 12:14:14 -04:00
Matt Wright f2d5245bd8 Import check to account for new version of MongoEngine 2013-05-03 12:13:58 -04:00
Matt Wright 97e1960abd Merge pull request #112 from poundifdef/login_flash
Make flask-login respect SECURITY_FLASH_MESSAGES
2013-04-14 13:40:57 -07:00
Jay Goel e749b77ca7 Make flask-login respect SECURITY_FLASH_MESSAGES 2013-04-14 16:37:23 -04:00
Matt Wright 6f3c163ee7 Merge pull request #111 from joshpurvis/issue110
Changed has_role to accept strings with mongoengine. Fixes #110
2013-04-14 10:05:34 -07:00
Josh Purvis 3b81ec57ea Changed has_role to accept strings with mongoengine. Fixes #110 2013-04-13 15:11:56 -04:00
Matt Wright 38874433c7 Add tests for Peewee support 2013-04-04 18:09:55 -04:00
Matt Wright 4eda3e756f PEP8 polish 2013-04-04 16:39:50 -04:00
Matt Wright 4815b1afed Make find_user method for MongoEngineUserDatastore add contraints to query 2013-04-04 15:50:46 -04:00
22 changed files with 195 additions and 88 deletions
+2 -2
View File
@@ -6,9 +6,9 @@ python:
- "pypy"
install:
- pip install . --quiet --use-mirrors
- 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 Flask-Mail py-bcrypt MySQL-python --quiet --use-mirrors
- pip install nose simplejson Flask-SQLAlchemy Flask-MongoEngine Flask-Peewee py-bcrypt MySQL-python --quiet
before_script:
- mysql -e 'create database flask_security_test;'
+46 -3
View File
@@ -3,6 +3,49 @@ Flask-Security Changelog
Here you can see the full list of changes between each Flask-Security release.
Version 1.6.7
-------------
Released July 11th 2013
- Made password length form error message configurable
- Fixed email confirmation bug that prevented logged in users from confirming their email
Version 1.6.6
-------------
Released June 28th 2013
- Fixed dependency versions
Version 1.6.5
-------------
Released June 20th 2013
- Fixed bug in `flask.ext.security.confirmable.generate_confirmation_link`
Version 1.6.4
-------------
Released June 18th 2013
- Added `SECURITY_DEFAULT_REMEMBER_ME` configuration value to unify behavior between endpoints
- Fixed Flask-Login dependency problem
- Added optional `next` parameter to registration endpoint, similar to that of login
Version 1.6.3
-------------
Released May 8th 2013
- Fixed bug in regards to imports with latest version of MongoEngine
Version 1.6.2
-------------
@@ -108,10 +151,10 @@ Version 1.2.0
Released March 12th 2012
- Added configuration option `SECURITY_FLASH_MESSAGES` which can be set to a
- Added configuration option `SECURITY_FLASH_MESSAGES` which can be set to a
boolean value to specify if Flask-Security should flash messages or not.
Version 1.1.0
-------------
Initial release
Initial release
+2 -2
View File
@@ -9,9 +9,9 @@
<ul>
<li><a href="http://pypi.python.org/pypi/Flask-Security">Flask-Security @ PyPI</a></li>
<li><a href="http://github.com/mattupstate/flask-security">Flask-Security @ github</a></li>
<li><a href="http://github.com/jfinkels/flask-security/issues">Issue Tracker</a></li>
<li><a href="http://github.com/mattupstate/flask-security/issues">Issue Tracker</a></li>
</ul>
<ul>
<li><a href="http://pypi.python.org/pypi/Flask-Social">Flask-Social</a></li>
<li><a href="http://github.com/mattupstate/flask-social">Flask-Social @ github</a></li>
</ul>
</ul>
+1 -1
View File
@@ -49,7 +49,7 @@ copyright = u'2012, Matt Wright'
# built documents.
#
# The short X.Y version.
version = '1.6.2'
version = '1.6.7'
# The full version, including alpha/beta/rc tags.
release = version
+7
View File
@@ -156,6 +156,10 @@ Feature Flags
sent an email with a login link. This feature is
experimental and should be used with caution. Defaults
to ``False``.
``SECURITY_CHANGEABLE`` Specifies if Flask-Security should enable the
change password endpoint. The URL for this endpoint is
specified by the ``SECURITY_CHANGE_URL`` configuration
option. Defaults to ``False``.
========================= ======================================================
Email
@@ -220,4 +224,7 @@ Miscellaneous
remember tokens. Remember tokens are
used instead of user ID's as it is more
secure. Defaults to ``remember-salt``.
``SECURITY_DEFAULT_REMEMBER_ME`` Specifies the default "remember me"
value used when logging in a user.
Defaults to ``False``.
======================================= ========================================
+1 -1
View File
@@ -10,7 +10,7 @@
:license: MIT, see LICENSE for more details.
"""
__version__ = '1.6.2'
__version__ = '1.6.7'
from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore, PeeweeUserDatastore
+1 -2
View File
@@ -14,8 +14,7 @@ from flask import current_app as app, request
from werkzeug.local import LocalProxy
from .signals import password_changed
from .utils import send_mail, encrypt_password, url_for_security, \
config_value
from .utils import send_mail, encrypt_password, config_value
# Convenient references
+2 -3
View File
@@ -15,7 +15,7 @@ from flask import current_app as app, request
from werkzeug.local import LocalProxy
from .utils import send_mail, md5, url_for_security, get_token_status,\
config_value
config_value
from .signals import user_confirmed, confirm_instructions_sent
@@ -27,8 +27,7 @@ _datastore = LocalProxy(lambda: _security.datastore)
def generate_confirmation_link(user):
token = generate_confirmation_token(user)
url = url_for_security('confirm_email', token=token)
return request.url_root[:-1] + url, token
return url_for_security('confirm_email', token=token, _external=True), token
def send_confirmation_instructions(user):
+22 -14
View File
@@ -10,10 +10,10 @@
"""
from flask import current_app
from flask.ext.login import AnonymousUser as AnonymousUserBase, \
UserMixin as BaseUserMixin, LoginManager, current_user
from flask.ext.login import AnonymousUserMixin, UserMixin as BaseUserMixin, \
LoginManager, current_user
from flask.ext.principal import Principal, RoleNeed, UserNeed, Identity, \
identity_loaded
identity_loaded
from itsdangerous import URLSafeTimedSerializer
from passlib.context import CryptContext
from werkzeug.datastructures import ImmutableList
@@ -22,8 +22,8 @@ from werkzeug.local import LocalProxy
from .utils import config_value as cv, get_config, md5, url_for_security
from .views import create_blueprint
from .forms import LoginForm, ConfirmRegisterForm, RegisterForm, \
ForgotPasswordForm, ChangePasswordForm, ResetPasswordForm, \
SendConfirmationForm, PasswordlessLoginForm
ForgotPasswordForm, ChangePasswordForm, ResetPasswordForm, \
SendConfirmationForm, PasswordlessLoginForm
# Convenient references
_security = LocalProxy(lambda: current_app.extensions['security'])
@@ -76,6 +76,7 @@ _default_config = {
'LOGIN_SALT': 'login-salt',
'CHANGE_SALT': 'change-salt',
'REMEMBER_SALT': 'remember-salt',
'DEFAULT_REMEMBER_ME': False,
'DEFAULT_HTTP_AUTH_REALM': 'Login Required',
'EMAIL_SUBJECT_REGISTER': 'Welcome',
'EMAIL_SUBJECT_CONFIRM': 'Please confirm your email',
@@ -109,6 +110,7 @@ _default_messages = {
'EMAIL_NOT_PROVIDED': ('Email not provided', 'error'),
'INVALID_EMAIL_ADDRESS': ('Invalid email address', 'error'),
'PASSWORD_NOT_PROVIDED': ('Password not provided', 'error'),
'PASSWORD_INVALID_LENGTH': ('Password must be at least 6 characters', 'error'),
'USER_DOES_NOT_EXIST': ('Specified user does not exist', 'error'),
'INVALID_PASSWORD': ('Invalid password', 'error'),
'PASSWORDLESS_LOGIN_SUCCESSFUL': ('You have successfuly logged in.', 'success'),
@@ -153,8 +155,7 @@ def _token_loader(token):
return user
except:
pass
return None
return AnonymousUser()
def _identity_loader():
@@ -179,8 +180,14 @@ def _get_login_manager(app):
lm.login_view = '%s.login' % cv('BLUEPRINT_NAME', app=app)
lm.user_loader(_user_loader)
lm.token_loader(_token_loader)
lm.login_message, lm.login_message_category = cv('MSG_LOGIN', app=app)
lm.needs_refresh_message, lm.needs_refresh_message_category = cv('MSG_REFRESH', app=app)
if cv('FLASH_MESSAGES', app=app):
lm.login_message, lm.login_message_category = cv('MSG_LOGIN', app=app)
lm.needs_refresh_message, lm.needs_refresh_message_category = cv('MSG_REFRESH', app=app)
else:
lm.login_message = None
lm.needs_refresh_message = None
lm.init_app(app)
return lm
@@ -241,8 +248,7 @@ class RoleMixin(object):
self.name == getattr(other, 'name', None))
def __ne__(self, other):
return (self.name != other and
self.name != getattr(other, 'name', None))
return not self.__eq__(other)
class UserMixin(BaseUserMixin):
@@ -261,14 +267,16 @@ class UserMixin(BaseUserMixin):
"""Returns `True` if the user identifies with the specified role.
:param role: A role name or `Role` instance"""
return role in self.roles
if isinstance(role, basestring):
return role in (role.name for role in self.roles)
else:
return role in self.roles
class AnonymousUser(AnonymousUserBase):
class AnonymousUser(AnonymousUserMixin):
"""AnonymousUser definition"""
def __init__(self):
super(AnonymousUser, self).__init__()
self.roles = ImmutableList()
def has_role(self, *args):
+20 -14
View File
@@ -95,12 +95,11 @@ class UserDatastore(object):
:param user: The user to manipulate
:param role: The role to add to the user
"""
rv = False
user, role = self._prepare_role_modify_args(user, role)
if role not in user.roles:
rv = True
user.roles.append(role)
return rv
return True
return False
def remove_role_from_user(self, user, role):
"""Removes a role from a user
@@ -191,7 +190,14 @@ class MongoEngineUserDatastore(MongoEngineDatastore, UserDatastore):
UserDatastore.__init__(self, user_model, role_model)
def find_user(self, **kwargs):
return self.user_model.objects(**kwargs).first()
try:
from mongoengine.queryset import Q, QCombination
except ImportError:
from mongoengine.queryset.visitor import Q, QCombination
queries = map(lambda i: Q(**{i[0]: i[1]}), kwargs.items())
query = QCombination(QCombination.AND, queries)
return self.user_model.objects(query).first()
def find_role(self, role):
return self.role_model.objects(name=role).first()
@@ -217,10 +223,7 @@ class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
return None
def find_role(self, role):
try:
return self.role_model.filter(name=role).get()
except self.role_model.DoesNotExist:
return None
return self.role_model.filter(name=role).get()
def create_user(self, **kwargs):
"""Creates and returns a new user from the given parameters."""
@@ -231,7 +234,6 @@ class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
self.add_role_to_user(user, role)
return user
def add_role_to_user(self, user, role):
"""Adds a role tp a user
@@ -239,10 +241,12 @@ class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
:param role: The role to add to the user
"""
user, role = self._prepare_role_modify_args(user, role)
if self.UserRole.select().where(self.UserRole.user==user, self.UserRole.role==role).count():
result = self.UserRole.select() \
.where(self.UserRole.user == user.id, self.UserRole.role == role.id)
if result.count():
return False
else:
self.UserRole.create(user=user, role=role)
self.UserRole.create(user=user.id, role=role.id)
return True
def remove_role_from_user(self, user, role):
@@ -252,9 +256,11 @@ class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
:param role: The role to remove from the user
"""
user, role = self._prepare_role_modify_args(user, role)
if self.UserRole.select().where(self.UserRole.user==user, self.UserRole.role==role).count():
self.UserRole.delete().where(self.UserRole.user==user, self.UserRole.role==role)
result = self.UserRole.select() \
.where(self.UserRole.user == user, self.UserRole.role == role)
if result.count():
self.UserRole.delete().where(
self.UserRole.user == user, self.UserRole.role == role)
return True
else:
return False
+4 -5
View File
@@ -16,7 +16,7 @@ import flask_wtf as wtf
from flask import request, current_app
from flask_wtf import Form as BaseForm, TextField, PasswordField, \
SubmitField, HiddenField, BooleanField, ValidationError, Field
SubmitField, HiddenField, BooleanField, ValidationError, Field
from flask_login import current_user
from werkzeug.local import LocalProxy
@@ -69,6 +69,7 @@ class Length(ValidatorMixin, wtf.Length):
email_required = Required(message='EMAIL_NOT_PROVIDED')
email_validator = Email(message='INVALID_EMAIL_ADDRESS')
password_required = Required(message='PASSWORD_NOT_PROVIDED')
password_length = Length(min=6, max=128, message='PASSWORD_INVALID_LENGTH')
def get_form_field_label(key):
@@ -122,8 +123,7 @@ class PasswordFormMixin():
class NewPasswordFormMixin():
password = PasswordField(get_form_field_label('password'),
validators=[password_required,
Length(min=6, max=128)])
validators=[password_required, password_length])
class PasswordConfirmFormMixin():
@@ -256,8 +256,7 @@ class ChangePasswordForm(Form, PasswordFormMixin):
"""The default change password form"""
new_password = PasswordField(get_form_field_label('new_password'),
validators=[password_required,
Length(min=6, max=128)])
validators=[password_required, password_length])
new_password_confirm = PasswordField(get_form_field_label('retype_password'),
validators=[EqualTo('new_password', message='RETYPE_PASSWORD_MISMATCH')])
+2 -3
View File
@@ -14,7 +14,7 @@ from werkzeug.local import LocalProxy
from .signals import login_instructions_sent
from .utils import send_mail, url_for_security, get_token_status, \
config_value
config_value
# Convenient references
@@ -30,8 +30,7 @@ def send_login_instructions(user):
:param token: The login token
"""
token = generate_login_token(user)
url = url_for_security('token_login', token=token)
login_link = request.url_root[:-1] + url
login_link = url_for_security('token_login', token=token, _external=True)
send_mail(config_value('EMAIL_SUBJECT_PASSWORDLESS'), user.email,
'login_instructions', user=user, login_link=login_link)
+2 -3
View File
@@ -14,7 +14,7 @@ from werkzeug.local import LocalProxy
from .signals import password_reset, reset_password_instructions_sent
from .utils import send_mail, md5, encrypt_password, url_for_security, \
get_token_status, config_value
get_token_status, config_value
# Convenient references
@@ -29,8 +29,7 @@ def send_reset_password_instructions(user):
:param user: The user to send the instructions to
"""
token = generate_reset_password_token(user)
url = url_for_security('reset_password', token=token)
reset_link = request.url_root[:-1] + url
reset_link = url_for_security('reset_password', token=token, _external=True)
send_mail(config_value('EMAIL_SUBJECT_PASSWORD_RESET'), user.email,
'reset_instructions',
+1 -1
View File
@@ -15,7 +15,7 @@ from werkzeug.local import LocalProxy
from .confirmable import generate_confirmation_link
from .signals import user_registered
from .utils import do_flash, get_message, send_mail, encrypt_password, \
config_value
config_value
# Convenient references
_security = LocalProxy(lambda: app.extensions['security'])
+16 -5
View File
@@ -14,12 +14,13 @@ import blinker
import functools
import hashlib
import hmac
from contextlib import contextmanager
from datetime import datetime, timedelta
from flask import url_for, flash, current_app, request, session, render_template
from flask.ext.login import login_user as _login_user, \
logout_user as _logout_user
logout_user as _logout_user
from flask.ext.mail import Message
from flask.ext.principal import Identity, AnonymousIdentity, identity_changed
from itsdangerous import BadSignature, SignatureExpired
@@ -37,9 +38,12 @@ _datastore = LocalProxy(lambda: _security.datastore)
_pwd_context = LocalProxy(lambda: _security.pwd_context)
def login_user(user, remember=True):
def login_user(user, remember=None):
"""Performs the login and sends the appropriate signal."""
if remember is None:
remember = config_value('DEFAULT_REMEMBER_ME')
if not _login_user(user, remember):
return False
@@ -144,11 +148,18 @@ def url_for_security(endpoint, **values):
return url_for(endpoint, **values)
def get_post_login_redirect():
"""Returns the URL to redirect to after a user logs in successfully."""
def get_post_action_redirect(config_key):
return (get_url(request.args.get('next')) or
get_url(request.form.get('next')) or
find_redirect('SECURITY_POST_LOGIN_VIEW'))
find_redirect(config_key))
def get_post_login_redirect():
return get_post_action_redirect('SECURITY_POST_LOGIN_VIEW')
def get_post_register_redirect():
return get_post_action_redirect('SECURITY_POST_REGISTER_VIEW')
def find_redirect(key):
+14 -15
View File
@@ -10,24 +10,23 @@
"""
from flask import current_app, redirect, request, render_template, jsonify, \
after_this_request, Blueprint
after_this_request, Blueprint
from flask_login import current_user
from werkzeug.datastructures import MultiDict
from werkzeug.local import LocalProxy
from .confirmable import send_confirmation_instructions, \
confirm_user, confirm_email_token_status
confirm_user, confirm_email_token_status
from .decorators import login_required, anonymous_user_required
from .passwordless import send_login_instructions, \
login_token_status
login_token_status
from .recoverable import reset_password_token_status, \
send_reset_password_instructions, update_password
send_reset_password_instructions, update_password
from .changeable import change_user_password
from .registerable import register_user
from .utils import get_url, get_post_login_redirect, do_flash, \
get_message, login_user, logout_user, url_for_security as url_for, \
config_value
from .utils import config_value, do_flash, get_url, get_post_login_redirect, \
get_post_register_redirect, get_message, login_user, logout_user, \
url_for_security as url_for
# Convenient references
_security = LocalProxy(lambda: current_app.extensions['security'])
@@ -123,9 +122,7 @@ def register():
login_user(user)
if not request.json:
post_register_url = get_url(_security.post_register_view)
post_login_url = get_url(_security.post_login_view)
return redirect(post_register_url or post_login_url)
return redirect(get_post_register_redirect())
if request.json:
return _render_json(form)
@@ -173,7 +170,7 @@ def token_login(token):
if invalid or expired:
return redirect(url_for('login'))
login_user(user, True)
login_user(user)
after_this_request(_commit)
do_flash(*get_message('PASSWORDLESS_LOGIN_SUCCESSFUL'))
@@ -203,7 +200,6 @@ def send_confirmation():
**_ctx('send_confirmation'))
@anonymous_user_required
def confirm_email(token):
"""View function which handles a email confirmation request."""
@@ -220,8 +216,11 @@ def confirm_email(token):
return redirect(get_url(_security.confirm_error_view) or
url_for('send_confirmation'))
if user != current_user:
logout_user()
login_user(user)
confirm_user(user)
login_user(user, True)
after_this_request(_commit)
do_flash(*get_message('EMAIL_CONFIRMED'))
@@ -272,7 +271,7 @@ def reset_password(token):
after_this_request(_commit)
update_password(user, form.password.data)
do_flash(*get_message('PASSWORD_RESET'))
login_user(user, True)
login_user(user)
return redirect(get_url(_security.post_reset_view) or
get_url(_security.post_login_view))
+2 -2
View File
@@ -20,7 +20,7 @@ from setuptools import setup
setup(
name='Flask-Security',
version='1.6.2',
version='1.6.7',
url='https://github.com/mattupstate/flask-security',
license='MIT',
author='Matt Wright',
@@ -35,7 +35,7 @@ setup(
platforms='any',
install_requires=[
'Flask>=0.9',
'Flask-Login>=0.1.3',
'Flask-Login>=0.2.3',
'Flask-Mail>=0.7.3',
'Flask-Principal>=0.3.3',
'Flask-WTF>=0.8',
+27 -1
View File
@@ -68,6 +68,14 @@ class ConfiguredSecurityTests(SecurityTest):
r = self._post('/register', data=data, follow_redirects=True)
self.assertIn('Post Register', r.data)
def test_register_with_next_querystring_argument(self):
data = dict(email='dude@lp.com',
password='password',
password_confirm='password')
r = self._post('/register?next=/page1', data=data, follow_redirects=True)
self.assertIn('Page 1', r.data)
def test_register_json(self):
data = '{ "email": "dude@lp.com", "password": "password", "csrf_token":"%s" }' % self.csrf_token
r = self._post('/register', data=data, content_type='application/json')
@@ -328,6 +336,24 @@ class LoginWithoutImmediateConfirmTests(SecurityTest):
r = self._post('/register', data=data, follow_redirects=True)
self.assertIn(e, r.data)
def test_confirm_email_of_user_different_than_current_user(self):
e1 = 'dude@lp.com'
e2 = 'lady@lp.com'
with capture_registrations() as registrations:
self.register(e1)
self.register(e2)
token1 = registrations[0]['confirm_token']
token2 = registrations[1]['confirm_token']
self.client.get('/confirm/' + token1, follow_redirects=True)
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)
class RecoverableTests(SecurityTest):
@@ -451,7 +477,7 @@ class ChangePasswordTest(SecurityTest):
'new_password_confirm': 'a'
}, follow_redirects=True)
self.assertNotIn('You successfully changed your password', r.data)
self.assertIn('Field must be between', r.data)
self.assertIn('Password must be at least 6 characters', r.data)
def test_change_password_success(self):
self.authenticate()
+5 -5
View File
@@ -60,7 +60,7 @@ class DefaultSecurityTests(SecurityTest):
def test_unauthorized_access(self):
r = self._get('/profile', follow_redirects=True)
self.assertIn('<li class="message">Please log in to access this page.</li>', r.data)
self.assertIn('<li class="info">Please log in to access this page.</li>', r.data)
def test_authorized_access(self):
self.authenticate()
@@ -231,11 +231,11 @@ class MongoEngineSecurityTests(DefaultSecurityTests):
return create_app(auth_config, **kwargs)
# class PeeweeSecurityTests(DefaultSecurityTests):
class PeeweeSecurityTests(DefaultSecurityTests):
# def _create_app(self, auth_config, **kwargs):
# from tests.test_app.peewee_app import create_app
# return create_app(auth_config, **kwargs)
def _create_app(self, auth_config, **kwargs):
from tests.test_app.peewee_app import create_app
return create_app(auth_config, **kwargs)
class DefaultDatastoreTests(SecurityTest):
+12 -5
View File
@@ -15,7 +15,14 @@ def compare_user(a, b):
return a.id == b.id and a.email == b.email and a.password == b.password
class RegisterableSignalsTests(SecurityTest):
class SignalTest(SecurityTest):
def _create_app(self, auth_config, **kwargs):
from tests.test_app.mongoengine import create_app
return create_app(auth_config, **kwargs)
class RegisterableSignalsTests(SignalTest):
AUTH_CONFIG = {
'SECURITY_CONFIRMABLE': True,
@@ -42,7 +49,7 @@ class RegisterableSignalsTests(SecurityTest):
self.assertEqual(mocks.signals_sent(), set())
class ConfirmableSignalsTests(SecurityTest):
class ConfirmableSignalsTests(SignalTest):
AUTH_CONFIG = {
'SECURITY_CONFIRMABLE': True,
@@ -103,7 +110,7 @@ class ConfirmableSignalsTests(SecurityTest):
self.assertEqual(mocks.signals_sent(), set())
class RecoverableSignalsTests(SecurityTest):
class RecoverableSignalsTests(SignalTest):
AUTH_CONFIG = {
'SECURITY_RECOVERABLE': True,
@@ -153,7 +160,7 @@ class RecoverableSignalsTests(SecurityTest):
self.assertEqual(mocks.signals_sent(), set())
class ChangeableSignalsTests(SecurityTest):
class ChangeableSignalsTests(SignalTest):
AUTH_CONFIG = {
'SECURITY_CHANGEABLE': True,
@@ -204,7 +211,7 @@ class ChangeableSignalsTests(SecurityTest):
self.assertEqual(mocks.signals_sent(), set())
class PasswordlessTests(SecurityTest):
class PasswordlessTests(SignalTest):
AUTH_CONFIG = {
'SECURITY_PASSWORDLESS': True
+4
View File
@@ -113,6 +113,10 @@ def create_app(config):
def invalid_role():
return 'success' if ds.find_role('bogus') is None else 'failure'
@app.route('/page1')
def page_1():
return 'Page 1'
return app
+2 -1
View File
@@ -14,11 +14,12 @@ from flask.ext.security import Security, UserMixin, RoleMixin, \
from tests.test_app import create_app as create_base_app, populate_data, \
add_context_processors
def create_app(config, **kwargs):
app = create_base_app(config)
app.config['DATABASE'] = {
'name': 'example2.db',
'engine': 'peewee.SqliteDatabase',
'engine': 'peewee.SqliteDatabase'
}
db = Database(app)