mirror of
https://github.com/wassname/flask-security.git
synced 2026-08-07 11:22:21 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e773232e5a | ||
|
|
f401eac496 | ||
|
|
f6b532103d | ||
|
|
27a4bbac6a | ||
|
|
0cf7d563dc | ||
|
|
7c906de427 | ||
|
|
9c03884441 | ||
|
|
4ed4579738 | ||
|
|
156ccaecc1 | ||
|
|
a5482922f3 | ||
|
|
d3cfddfcac | ||
|
|
312a527498 | ||
|
|
d87765fc3b | ||
|
|
d05e699a15 | ||
|
|
e7855488df | ||
|
|
01bdc0d356 | ||
|
|
5e2a016bcd |
@@ -3,6 +3,15 @@ Flask-Security Changelog
|
||||
|
||||
Here you can see the full list of changes between each Flask-Security release.
|
||||
|
||||
Version 1.6.8
|
||||
-------------
|
||||
|
||||
Released August 1st 2013
|
||||
|
||||
- Fixed bug with case sensitivity of email address during login
|
||||
- Code cleanup regarding token_callback
|
||||
- Ignore validation errors in find_user function for MongoEngineUserDatastore
|
||||
|
||||
Version 1.6.7
|
||||
-------------
|
||||
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ copyright = u'2012, Matt Wright'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.6.7'
|
||||
version = '1.6.8'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
||||
+33
-19
@@ -56,6 +56,8 @@ URLs and Views
|
||||
``/register``.
|
||||
``SECURITY_RESET_URL`` Specifies the password reset URL. Defaults to
|
||||
``/reset``.
|
||||
``SECURITY_CHANGE_URL`` Specifies the password change URL. Defaults to
|
||||
``/change``.
|
||||
``SECURITY_CONFIRM_URL`` Specifies the email confirmation URL. Defaults
|
||||
to ``/confirm``.
|
||||
``SECURITY_POST_LOGIN_VIEW`` Specifies the default view to redirect to after
|
||||
@@ -86,8 +88,14 @@ URLs and Views
|
||||
successfully resets their password. This value
|
||||
can be set to a URL or an endpoint name. If this
|
||||
value is ``None`` the user is redirected to the
|
||||
value of ``SECURITY_POST_LOGIN_VIEW``. Defaults to
|
||||
``None``.
|
||||
value of ``SECURITY_POST_LOGIN_VIEW``. Defaults
|
||||
to ``None``.
|
||||
``SECURITY_POST_CHANGE_VIEW`` Specifies the view to redirect to after a user
|
||||
successfully changes their password. This value
|
||||
can be set to a URL or an endpoint name. If this
|
||||
value is ``None`` the user is redirected to the
|
||||
value of ``SECURITY_POST_LOGIN_VIEW``. Defaults
|
||||
to ``None``.
|
||||
``SECURITY_UNAUTHORIZED_VIEW`` Specifies the view to redirect to if a user
|
||||
attempts to access a URL/endpoint that they do
|
||||
not have permission to access. If this value is
|
||||
@@ -167,23 +175,29 @@ Email
|
||||
|
||||
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
|
||||
|
||||
=========================================== ====================================
|
||||
``SECURITY_EMAIL_SUBJECT_REGISTER`` Sets the subject for the
|
||||
confirmation email. Defaults to
|
||||
``Welcome``
|
||||
``SECURITY_EMAIL_SUBJECT_PASSWORDLESS`` Sets the subject for the
|
||||
passwordless feature. Defaults to
|
||||
``Login instructions``
|
||||
``SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE`` Sets subject for the password
|
||||
notice. Defaults to
|
||||
``Your password has been reset``
|
||||
``SECURITY_EMAIL_SUBJECT_PASSWORD_RESET`` Sets the subject for the password
|
||||
reset. Defaults to
|
||||
``Password reset instructions``
|
||||
``SECURITY_EMAIL_SUBJECT_CONFIRM`` Sets the subject for the email
|
||||
confirmation message. Defaults to
|
||||
``Please confirm your email``
|
||||
=========================================== ====================================
|
||||
================================================= ==============================
|
||||
``SECURITY_EMAIL_SUBJECT_REGISTER`` Sets the subject for the
|
||||
confirmation email. Defaults
|
||||
to ``Welcome``
|
||||
``SECURITY_EMAIL_SUBJECT_PASSWORDLESS`` Sets the subject for the
|
||||
passwordless feature. Defaults
|
||||
to ``Login instructions``
|
||||
``SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE`` Sets subject for the password
|
||||
notice. Defaults to ``Your
|
||||
password has been reset``
|
||||
``SECURITY_EMAIL_SUBJECT_PASSWORD_RESET`` Sets the subject for the
|
||||
password reset email. Defaults
|
||||
to ``Password reset
|
||||
instructions``
|
||||
``SECURITY_EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE`` Sets the subject for the
|
||||
password change notice.
|
||||
Defaults to ``Your password
|
||||
has been changed``
|
||||
``SECURITY_EMAIL_SUBJECT_CONFIRM`` Sets the subject for the email
|
||||
confirmation message. Defaults
|
||||
to ``Please confirm your
|
||||
email``
|
||||
================================================= ==============================
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
+16
-1
@@ -34,7 +34,8 @@ Password encryption is enabled with `passlib`_. Passwords are stored in plain
|
||||
text by default but you can easily configure the encryption algorithm. You
|
||||
should **always use an encryption algorithm** in your production environment.
|
||||
You may also specify to use HMAC with a configured salt value in addition to the
|
||||
algorithm chosen. Bear in mind passlib does not assume which algorithm you will choose and may require additional libraries to be installed.
|
||||
algorithm chosen. Bear in mind passlib does not assume which algorithm you will
|
||||
choose and may require additional libraries to be installed.
|
||||
|
||||
|
||||
Basic HTTP Authentication
|
||||
@@ -104,6 +105,20 @@ statistics. They include:
|
||||
* Total login count
|
||||
|
||||
|
||||
JSON/Ajax Support
|
||||
-----------------
|
||||
|
||||
Flask-Security supports JSON/Ajax requests where appropriate. Just remember that
|
||||
all endpoints require a CSRF token just like HTML views. More specifically
|
||||
JSON is supported for the following operations:
|
||||
|
||||
* Login requests
|
||||
* Registration requests
|
||||
* Change password requests
|
||||
* Confirmation requests
|
||||
* Forgot password requests
|
||||
* Passwordless login requests
|
||||
|
||||
|
||||
.. _Flask-Login: http://packages.python.org/Flask-Login/
|
||||
.. _alternative token: http://packages.python.org/Flask-Login/#alternative-tokens
|
||||
|
||||
@@ -13,6 +13,7 @@ Flask application. They include:
|
||||
7. Token based password recovery / resetting (optional)
|
||||
8. User registration (optional)
|
||||
9. Login tracking (optional)
|
||||
10. JSON/Ajax Support
|
||||
|
||||
Many of these features are made possible by integrating various Flask extensions
|
||||
and libraries. They include:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
__version__ = '1.6.7'
|
||||
__version__ = '1.6.8'
|
||||
|
||||
from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
|
||||
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore, PeeweeUserDatastore
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask import current_app as app, request
|
||||
from flask import current_app as app
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from .signals import password_changed
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from flask import current_app as app, request
|
||||
from flask import current_app as app
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from .utils import send_mail, md5, url_for_security, get_token_status,\
|
||||
|
||||
@@ -244,7 +244,7 @@ def _context_processor():
|
||||
class RoleMixin(object):
|
||||
"""Mixin for `Role` model definitions"""
|
||||
def __eq__(self, other):
|
||||
return (self.name == other or \
|
||||
return (self.name == other or
|
||||
self.name == getattr(other, 'name', None))
|
||||
|
||||
def __ne__(self, other):
|
||||
@@ -346,10 +346,10 @@ class Security(object):
|
||||
self._state = self.init_app(app, datastore, **kwargs)
|
||||
|
||||
def init_app(self, app, datastore=None, register_blueprint=True,
|
||||
login_form=None, confirm_register_form=None,
|
||||
register_form=None, forgot_password_form=None,
|
||||
reset_password_form=None, change_password_form=None,
|
||||
send_confirmation_form=None, passwordless_login_form=None):
|
||||
login_form=None, confirm_register_form=None,
|
||||
register_form=None, forgot_password_form=None,
|
||||
reset_password_form=None, change_password_form=None,
|
||||
send_confirmation_form=None, passwordless_login_form=None):
|
||||
"""Initializes the Flask-Security extension for the specified
|
||||
application and datastore implentation.
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
class Datastore(object):
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
@@ -81,6 +82,10 @@ class UserDatastore(object):
|
||||
kwargs['roles'] = roles
|
||||
return kwargs
|
||||
|
||||
def get_user(self, id_or_email):
|
||||
"""Returns a user matching the specified ID or email address"""
|
||||
raise NotImplementedError
|
||||
|
||||
def find_user(self, *args, **kwargs):
|
||||
"""Returns a user matching the provided parameters."""
|
||||
raise NotImplementedError
|
||||
@@ -174,6 +179,11 @@ class SQLAlchemyUserDatastore(SQLAlchemyDatastore, UserDatastore):
|
||||
SQLAlchemyDatastore.__init__(self, db)
|
||||
UserDatastore.__init__(self, user_model, role_model)
|
||||
|
||||
def get_user(self, id_or_email):
|
||||
return (self.user_model.query.get(id_or_email) or
|
||||
self.user_model.query.filter(
|
||||
self.user_model.email.ilike(id_or_email)).first())
|
||||
|
||||
def find_user(self, **kwargs):
|
||||
return self.user_model.query.filter_by(**kwargs).first()
|
||||
|
||||
@@ -189,15 +199,26 @@ class MongoEngineUserDatastore(MongoEngineDatastore, UserDatastore):
|
||||
MongoEngineDatastore.__init__(self, db)
|
||||
UserDatastore.__init__(self, user_model, role_model)
|
||||
|
||||
def get_user(self, id_or_email):
|
||||
from mongoengine import ValidationError
|
||||
try:
|
||||
return self.user_model.objects(id=id_or_email).first()
|
||||
except ValidationError:
|
||||
return self.user_model.objects(email__iexact=id_or_email).first()
|
||||
|
||||
def find_user(self, **kwargs):
|
||||
try:
|
||||
from mongoengine.queryset import Q, QCombination
|
||||
except ImportError:
|
||||
from mongoengine.queryset.visitor import Q, QCombination
|
||||
from mongoengine.errors import ValidationError
|
||||
|
||||
queries = map(lambda i: Q(**{i[0]: i[1]}), kwargs.items())
|
||||
query = QCombination(QCombination.AND, queries)
|
||||
return self.user_model.objects(query).first()
|
||||
try:
|
||||
return self.user_model.objects(query).first()
|
||||
except ValidationError:
|
||||
return None
|
||||
|
||||
def find_role(self, role):
|
||||
return self.role_model.objects(name=role).first()
|
||||
@@ -216,6 +237,17 @@ class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
|
||||
UserDatastore.__init__(self, user_model, role_model)
|
||||
self.UserRole = role_link
|
||||
|
||||
def get_user(self, id_or_email):
|
||||
try:
|
||||
return self.user_model.get(self.user_model.id == id_or_email)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return self.user_model.get(self.user_model.email ** id_or_email)
|
||||
except self.user_model.DoesNotExist:
|
||||
pass
|
||||
return None
|
||||
|
||||
def find_user(self, **kwargs):
|
||||
try:
|
||||
return self.user_model.filter(**kwargs).get()
|
||||
|
||||
@@ -53,21 +53,17 @@ def _check_token():
|
||||
token = request.args.get(args_key, header_token)
|
||||
if request.json:
|
||||
token = request.json.get(args_key, token)
|
||||
serializer = _security.remember_token_serializer
|
||||
|
||||
try:
|
||||
data = serializer.loads(token)
|
||||
except:
|
||||
return False
|
||||
user = _security.login_manager.token_callback(token)
|
||||
|
||||
user = _security.datastore.find_user(id=data[0])
|
||||
|
||||
if utils.md5(user.password) == data[1]:
|
||||
if user and user.is_authenticated():
|
||||
app = current_app._get_current_object()
|
||||
_request_ctx_stack.top.user = user
|
||||
identity_changed.send(app, identity=Identity(user.id))
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _check_http_auth():
|
||||
auth = request.authorization or BasicAuth(username=None, password=None)
|
||||
|
||||
+18
-17
@@ -96,33 +96,32 @@ class Form(BaseForm):
|
||||
|
||||
|
||||
class EmailFormMixin():
|
||||
email = TextField(get_form_field_label('email'),
|
||||
validators=[email_required,
|
||||
email_validator])
|
||||
email = TextField(
|
||||
get_form_field_label('email'),
|
||||
validators=[email_required, email_validator])
|
||||
|
||||
|
||||
class UserEmailFormMixin():
|
||||
user = None
|
||||
email = TextField(get_form_field_label('email'),
|
||||
validators=[email_required,
|
||||
email_validator,
|
||||
valid_user_email])
|
||||
email = TextField(
|
||||
get_form_field_label('email'),
|
||||
validators=[email_required, email_validator, valid_user_email])
|
||||
|
||||
|
||||
class UniqueEmailFormMixin():
|
||||
email = TextField(get_form_field_label('email'),
|
||||
validators=[email_required,
|
||||
email_validator,
|
||||
unique_user_email])
|
||||
email = TextField(
|
||||
get_form_field_label('email'),
|
||||
validators=[email_required, email_validator, unique_user_email])
|
||||
|
||||
|
||||
class PasswordFormMixin():
|
||||
password = PasswordField(get_form_field_label('password'),
|
||||
validators=[password_required])
|
||||
password = PasswordField(
|
||||
get_form_field_label('password'), validators=[password_required])
|
||||
|
||||
|
||||
class NewPasswordFormMixin():
|
||||
password = PasswordField(get_form_field_label('password'),
|
||||
password = PasswordField(
|
||||
get_form_field_label('password'),
|
||||
validators=[password_required, password_length])
|
||||
|
||||
|
||||
@@ -220,7 +219,7 @@ class LoginForm(Form, NextFormMixin):
|
||||
self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0])
|
||||
return False
|
||||
|
||||
self.user = _datastore.find_user(email=self.email.data)
|
||||
self.user = _datastore.get_user(self.email.data)
|
||||
|
||||
if self.user is None:
|
||||
self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
|
||||
@@ -255,10 +254,12 @@ class ResetPasswordForm(Form, NewPasswordFormMixin, PasswordConfirmFormMixin):
|
||||
class ChangePasswordForm(Form, PasswordFormMixin):
|
||||
"""The default change password form"""
|
||||
|
||||
new_password = PasswordField(get_form_field_label('new_password'),
|
||||
new_password = PasswordField(
|
||||
get_form_field_label('new_password'),
|
||||
validators=[password_required, password_length])
|
||||
|
||||
new_password_confirm = PasswordField(get_form_field_label('retype_password'),
|
||||
new_password_confirm = PasswordField(
|
||||
get_form_field_label('retype_password'),
|
||||
validators=[EqualTo('new_password', message='RETYPE_PASSWORD_MISMATCH')])
|
||||
|
||||
submit = SubmitField(get_form_field_label('change_password'))
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask import request, current_app as app
|
||||
from flask import current_app as app
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from .signals import login_instructions_sent
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask import current_app as app, request
|
||||
from flask import current_app as app
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from .signals import password_reset, reset_password_instructions_sent
|
||||
@@ -67,6 +67,7 @@ def reset_password_token_status(token):
|
||||
"""
|
||||
return get_token_status(token, 'reset', 'RESET_PASSWORD')
|
||||
|
||||
|
||||
def update_password(user, password):
|
||||
"""Update the specified user's password
|
||||
|
||||
|
||||
@@ -38,6 +38,6 @@ def register_user(**kwargs):
|
||||
|
||||
if config_value('SEND_REGISTER_EMAIL'):
|
||||
send_mail(config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome',
|
||||
user=user, confirmation_link=confirmation_link)
|
||||
user=user, confirmation_link=confirmation_link)
|
||||
|
||||
return user
|
||||
|
||||
@@ -78,8 +78,9 @@ def get_hmac(password):
|
||||
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 '
|
||||
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)
|
||||
@@ -386,5 +387,3 @@ def capture_signals():
|
||||
confirm_instructions_sent, login_instructions_sent,
|
||||
password_reset, password_changed,
|
||||
reset_password_instructions_sent])
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='Flask-Security',
|
||||
version='1.6.7',
|
||||
version='1.6.8',
|
||||
url='https://github.com/mattupstate/flask-security',
|
||||
license='MIT',
|
||||
author='Matt Wright',
|
||||
|
||||
+7
-6
@@ -30,7 +30,8 @@ class SecurityTest(TestCase):
|
||||
session['csrf'] = 'csrf_token'
|
||||
|
||||
csrf_hmac = hmac.new(self.app.config['SECRET_KEY'],
|
||||
'csrf_token'.encode('utf8'), digestmod=sha1)
|
||||
'csrf_token'.encode('utf8'),
|
||||
digestmod=sha1)
|
||||
self.csrf_token = '##' + csrf_hmac.hexdigest()
|
||||
|
||||
def _create_app(self, auth_config, **kwargs):
|
||||
@@ -38,17 +39,17 @@ class SecurityTest(TestCase):
|
||||
|
||||
def _get(self, route, content_type=None, follow_redirects=None, headers=None):
|
||||
return self.client.get(route, follow_redirects=follow_redirects,
|
||||
content_type=content_type or 'text/html',
|
||||
headers=headers)
|
||||
content_type=content_type or 'text/html',
|
||||
headers=headers)
|
||||
|
||||
def _post(self, route, data=None, content_type=None, follow_redirects=True, headers=None):
|
||||
if isinstance(data, dict):
|
||||
data['csrf_token'] = self.csrf_token
|
||||
|
||||
content_type = content_type or 'application/x-www-form-urlencoded'
|
||||
return self.client.post(route, data=data,
|
||||
follow_redirects=follow_redirects,
|
||||
content_type=content_type or 'application/x-www-form-urlencoded',
|
||||
headers=headers)
|
||||
follow_redirects=follow_redirects,
|
||||
content_type=content_type, headers=headers)
|
||||
|
||||
def register(self, email, password='password'):
|
||||
data = dict(email=email, password=password, csrf_token=self.csrf_token)
|
||||
|
||||
+22
-26
@@ -122,7 +122,6 @@ class DefaultTemplatePathTests(SecurityTest):
|
||||
'SECURITY_LOGIN_USER_TEMPLATE': 'custom_security/login_user.html',
|
||||
}
|
||||
|
||||
|
||||
def test_login_user_template(self):
|
||||
r = self._get('/login')
|
||||
|
||||
@@ -155,9 +154,8 @@ class RecoverableTemplatePathTests(SecurityTest):
|
||||
|
||||
def test_reset_password_template(self):
|
||||
with capture_reset_password_requests() as requests:
|
||||
r = self._post('/reset',
|
||||
data=dict(email='joe@lp.com'),
|
||||
follow_redirects=True)
|
||||
r = self._post('/reset', data=dict(email='joe@lp.com'),
|
||||
follow_redirects=True)
|
||||
|
||||
t = requests[0]['token']
|
||||
|
||||
@@ -365,9 +363,8 @@ class RecoverableTests(SecurityTest):
|
||||
|
||||
def test_reset_view(self):
|
||||
with capture_reset_password_requests() as requests:
|
||||
r = self._post('/reset',
|
||||
data=dict(email='joe@lp.com'),
|
||||
follow_redirects=True)
|
||||
r = self._post('/reset', data=dict(email='joe@lp.com'),
|
||||
follow_redirects=True)
|
||||
t = requests[0]['token']
|
||||
r = self._get('/reset/' + t)
|
||||
self.assertIn('<h1>Reset password</h1>', r.data)
|
||||
@@ -380,20 +377,18 @@ class RecoverableTests(SecurityTest):
|
||||
|
||||
def test_forgot_password_json(self):
|
||||
r = self._post('/reset', data='{"email": "matt@lp.com"}',
|
||||
content_type="application/json")
|
||||
content_type="application/json")
|
||||
self.assertEquals(r.status_code, 200)
|
||||
|
||||
def test_forgot_password_invalid_email(self):
|
||||
r = self._post('/reset',
|
||||
data=dict(email='larry@lp.com'),
|
||||
follow_redirects=True)
|
||||
r = self._post('/reset', data=dict(email='larry@lp.com'),
|
||||
follow_redirects=True)
|
||||
self.assertIn("Specified user does not exist", r.data)
|
||||
|
||||
def test_reset_password_with_valid_token(self):
|
||||
with capture_reset_password_requests() as requests:
|
||||
r = self._post('/reset',
|
||||
data=dict(email='joe@lp.com'),
|
||||
follow_redirects=True)
|
||||
r = self._post('/reset', data=dict(email='joe@lp.com'),
|
||||
follow_redirects=True)
|
||||
t = requests[0]['token']
|
||||
|
||||
r = self._post('/reset/' + t, data={
|
||||
@@ -480,13 +475,15 @@ class ChangePasswordTest(SecurityTest):
|
||||
self.assertIn('Password must be at least 6 characters', r.data)
|
||||
|
||||
def test_change_password_success(self):
|
||||
data = {
|
||||
'password': 'password',
|
||||
'new_password': 'newpassword',
|
||||
'new_password_confirm': 'newpassword'
|
||||
}
|
||||
|
||||
self.authenticate()
|
||||
with self.app.extensions['mail'].record_messages() as outbox:
|
||||
r = self._post('/change', data={
|
||||
'password': 'password',
|
||||
'new_password': 'newpassword',
|
||||
'new_password_confirm': 'newpassword'
|
||||
}, follow_redirects=True)
|
||||
r = self._post('/change', data=data, follow_redirects=True)
|
||||
|
||||
self.assertIn('You successfully changed your password', r.data)
|
||||
self.assertIn('Home Page', r.data)
|
||||
@@ -504,12 +501,13 @@ class ChangePasswordPostViewTest(SecurityTest):
|
||||
}
|
||||
|
||||
def test_change_password_success(self):
|
||||
data = {
|
||||
'password': 'password',
|
||||
'new_password': 'newpassword',
|
||||
'new_password_confirm': 'newpassword'
|
||||
}
|
||||
self.authenticate()
|
||||
r = self._post('/change', data={
|
||||
'password': 'password',
|
||||
'new_password': 'newpassword',
|
||||
'new_password_confirm': 'newpassword'
|
||||
}, follow_redirects=True)
|
||||
r = self._post('/change', data=data, follow_redirects=True)
|
||||
|
||||
self.assertIn('Profile Page', r.data)
|
||||
|
||||
@@ -792,8 +790,6 @@ class ConfirmableExtendFormsTest(SecurityTest):
|
||||
r = self._get('/register', follow_redirects=True)
|
||||
self.assertIn("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)
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ class DefaultSecurityTests(SecurityTest):
|
||||
r = self.authenticate()
|
||||
self.assertIn('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)
|
||||
|
||||
def test_unprovided_username(self):
|
||||
r = self.authenticate("")
|
||||
self.assertIn(self.get_message('EMAIL_NOT_PROVIDED'), r.data)
|
||||
@@ -59,6 +63,7 @@ class DefaultSecurityTests(SecurityTest):
|
||||
self.assertIsHomePage(r.data)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user