mirror of
https://github.com/wassname/flask-security.git
synced 2026-08-08 11:19:18 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b474efd7a | ||
|
|
f12bc17e7e | ||
|
|
999f882f61 | ||
|
|
a140c01b57 | ||
|
|
76cf3eaf6a | ||
|
|
a6b5d3053c | ||
|
|
0a48997fdd | ||
|
|
96f1b3e0d1 | ||
|
|
0facdaacd9 | ||
|
|
9cadf855a4 |
@@ -4,6 +4,7 @@ python:
|
|||||||
- "2.6"
|
- "2.6"
|
||||||
- "2.7"
|
- "2.7"
|
||||||
- "3.3"
|
- "3.3"
|
||||||
|
- "3.4"
|
||||||
- "pypy"
|
- "pypy"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
|||||||
@@ -3,6 +3,18 @@ Flask-Security Changelog
|
|||||||
|
|
||||||
Here you can see the full list of changes between each Flask-Security release.
|
Here you can see the full list of changes between each Flask-Security release.
|
||||||
|
|
||||||
|
Version 1.7.3
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Released June 10th 2014
|
||||||
|
|
||||||
|
- Fixed a bug where redirection to `SECURITY_POST_LOGIN_VIEW` was not respected
|
||||||
|
- Fixed string encoding in various places to be friendly to unicode
|
||||||
|
- Now using `werkzeug.security.safe_str_cmp` to check tokens
|
||||||
|
- Removed user information from JSON output on `/reset` responses
|
||||||
|
- Added Python 3.4 support
|
||||||
|
|
||||||
|
|
||||||
Version 1.7.2
|
Version 1.7.2
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ copyright = u'2012, Matt Wright'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '1.7.2'
|
version = '1.7.3'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = version
|
release = version
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
:license: MIT, see LICENSE for more details.
|
:license: MIT, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = '1.7.2'
|
__version__ = '1.7.3'
|
||||||
|
|
||||||
from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
|
from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
|
||||||
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore, PeeweeUserDatastore
|
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore, PeeweeUserDatastore
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from itsdangerous import URLSafeTimedSerializer
|
|||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
from werkzeug.datastructures import ImmutableList
|
from werkzeug.datastructures import ImmutableList
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
|
from werkzeug.security import safe_str_cmp
|
||||||
|
|
||||||
from .utils import config_value as cv, get_config, md5, url_for_security, string_types
|
from .utils import config_value as cv, get_config, md5, url_for_security, string_types
|
||||||
from .views import create_blueprint
|
from .views import create_blueprint
|
||||||
@@ -193,7 +194,7 @@ def _token_loader(token):
|
|||||||
try:
|
try:
|
||||||
data = _security.remember_token_serializer.loads(token)
|
data = _security.remember_token_serializer.loads(token)
|
||||||
user = _security.datastore.find_user(id=data[0])
|
user = _security.datastore.find_user(id=data[0])
|
||||||
if user and md5(user.password) == data[1]:
|
if user and safe_str_cmp(md5(user.password), data[1]):
|
||||||
return user
|
return user
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|||||||
+16
-4
@@ -99,13 +99,15 @@ def get_hmac(password):
|
|||||||
|
|
||||||
:param password: The password to sign
|
:param password: The password to sign
|
||||||
"""
|
"""
|
||||||
if _security.password_salt is None:
|
salt = _security.password_salt
|
||||||
|
|
||||||
|
if salt is None:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'The configuration value `SECURITY_PASSWORD_SALT` must '
|
'The configuration value `SECURITY_PASSWORD_SALT` must '
|
||||||
'not be None when the value of `SECURITY_PASSWORD_HASH` is '
|
'not be None when the value of `SECURITY_PASSWORD_HASH` is '
|
||||||
'set to "%s"' % _security.password_hash)
|
'set to "%s"' % _security.password_hash)
|
||||||
|
|
||||||
h = hmac.new(_security.password_salt.encode('utf-8'), password.encode('utf-8'), hashlib.sha512)
|
h = hmac.new(encode_string(salt), encode_string(password), hashlib.sha512)
|
||||||
return base64.b64encode(h.digest())
|
return base64.b64encode(h.digest())
|
||||||
|
|
||||||
|
|
||||||
@@ -149,8 +151,18 @@ def encrypt_password(password):
|
|||||||
return _pwd_context.encrypt(signed)
|
return _pwd_context.encrypt(signed)
|
||||||
|
|
||||||
|
|
||||||
|
def encode_string(string):
|
||||||
|
"""Encodes a string to bytes, if it isn't already.
|
||||||
|
|
||||||
|
:param string: The string to encode"""
|
||||||
|
|
||||||
|
if isinstance(string, text_type):
|
||||||
|
string = string.encode('utf-8')
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
def md5(data):
|
def md5(data):
|
||||||
return hashlib.md5(data.encode('ascii')).hexdigest()
|
return hashlib.md5(encode_string(data)).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def do_flash(message, category=None):
|
def do_flash(message, category=None):
|
||||||
@@ -196,7 +208,7 @@ def url_for_security(endpoint, **values):
|
|||||||
|
|
||||||
|
|
||||||
def validate_redirect_url(url):
|
def validate_redirect_url(url):
|
||||||
if url is None:
|
if url is None or url.strip() == '':
|
||||||
return False
|
return False
|
||||||
url_next = urlsplit(url)
|
url_next = urlsplit(url)
|
||||||
url_base = urlsplit(request.host_url)
|
url_base = urlsplit(request.host_url)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ _security = LocalProxy(lambda: current_app.extensions['security'])
|
|||||||
_datastore = LocalProxy(lambda: _security.datastore)
|
_datastore = LocalProxy(lambda: _security.datastore)
|
||||||
|
|
||||||
|
|
||||||
def _render_json(form, include_auth_token=False):
|
def _render_json(form, include_user=True, include_auth_token=False):
|
||||||
has_errors = len(form.errors) > 0
|
has_errors = len(form.errors) > 0
|
||||||
|
|
||||||
if has_errors:
|
if has_errors:
|
||||||
@@ -42,7 +42,9 @@ def _render_json(form, include_auth_token=False):
|
|||||||
response = dict(errors=form.errors)
|
response = dict(errors=form.errors)
|
||||||
else:
|
else:
|
||||||
code = 200
|
code = 200
|
||||||
response = dict(user=dict(id=str(form.user.id)))
|
response = dict()
|
||||||
|
if include_user:
|
||||||
|
response['user'] = dict(id=str(form.user.id))
|
||||||
if include_auth_token:
|
if include_auth_token:
|
||||||
token = form.user.get_auth_token()
|
token = form.user.get_auth_token()
|
||||||
response['user']['authentication_token'] = token
|
response['user']['authentication_token'] = token
|
||||||
@@ -78,7 +80,7 @@ def login():
|
|||||||
return redirect(get_post_login_redirect(form.next.data))
|
return redirect(get_post_login_redirect(form.next.data))
|
||||||
|
|
||||||
if request.json:
|
if request.json:
|
||||||
return _render_json(form, True)
|
return _render_json(form, include_auth_token=True)
|
||||||
|
|
||||||
return _security.render_template(config_value('LOGIN_USER_TEMPLATE'),
|
return _security.render_template(config_value('LOGIN_USER_TEMPLATE'),
|
||||||
login_user_form=form,
|
login_user_form=form,
|
||||||
@@ -121,7 +123,7 @@ def register():
|
|||||||
|
|
||||||
if not request.json:
|
if not request.json:
|
||||||
return redirect(get_post_register_redirect())
|
return redirect(get_post_register_redirect())
|
||||||
return _render_json(form, True)
|
return _render_json(form, include_auth_token=True)
|
||||||
|
|
||||||
if request.json:
|
if request.json:
|
||||||
return _render_json(form)
|
return _render_json(form)
|
||||||
@@ -247,7 +249,7 @@ def forgot_password():
|
|||||||
do_flash(*get_message('PASSWORD_RESET_REQUEST', email=form.user.email))
|
do_flash(*get_message('PASSWORD_RESET_REQUEST', email=form.user.email))
|
||||||
|
|
||||||
if request.json:
|
if request.json:
|
||||||
return _render_json(form)
|
return _render_json(form, include_user=False)
|
||||||
|
|
||||||
return _security.render_template(config_value('FORGOT_PASSWORD_TEMPLATE'),
|
return _security.render_template(config_value('FORGOT_PASSWORD_TEMPLATE'),
|
||||||
forgot_password_form=form,
|
forgot_password_form=form,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class PyTest(TestCommand):
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='Flask-Security',
|
name='Flask-Security',
|
||||||
version='1.7.2',
|
version='1.7.3',
|
||||||
url='https://github.com/mattupstate/flask-security',
|
url='https://github.com/mattupstate/flask-security',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
author='Matt Wright',
|
author='Matt Wright',
|
||||||
|
|||||||
@@ -23,11 +23,13 @@ def test_view_configuration(client):
|
|||||||
response = client.get('/custom_login')
|
response = client.get('/custom_login')
|
||||||
assert b"<h1>Login</h1>" in response.data
|
assert b"<h1>Login</h1>" in response.data
|
||||||
|
|
||||||
response = authenticate(client, endpoint='/custom_login', follow_redirects=True)
|
response = authenticate(client, endpoint='/custom_login')
|
||||||
assert b'Post Login' in response.data
|
assert 'location' in response.headers
|
||||||
|
assert response.headers['Location'] == 'http://localhost/post_login'
|
||||||
|
|
||||||
response = logout(client, endpoint='/custom_logout', follow_redirects=True)
|
response = logout(client, endpoint='/custom_logout')
|
||||||
assert b'Post Logout' in response.data
|
assert 'location' in response.headers
|
||||||
|
assert response.headers['Location'] == 'http://localhost/post_logout'
|
||||||
|
|
||||||
response = client.get('/http', headers={
|
response = client.get('/http', headers={
|
||||||
'Authorization': 'Basic %s' % base64.b64encode(b"joe@lp.com:bogus")
|
'Authorization': 'Basic %s' % base64.b64encode(b"joe@lp.com:bogus")
|
||||||
|
|||||||
+17
-1
@@ -12,7 +12,7 @@ from flask_security import Security
|
|||||||
from flask_security.forms import LoginForm, RegisterForm, ConfirmRegisterForm, \
|
from flask_security.forms import LoginForm, RegisterForm, ConfirmRegisterForm, \
|
||||||
SendConfirmationForm, PasswordlessLoginForm, ForgotPasswordForm, ResetPasswordForm, \
|
SendConfirmationForm, PasswordlessLoginForm, ForgotPasswordForm, ResetPasswordForm, \
|
||||||
ChangePasswordForm, TextField, PasswordField, email_required, email_validator, valid_user_email
|
ChangePasswordForm, TextField, PasswordField, email_required, email_validator, valid_user_email
|
||||||
from flask_security.utils import capture_reset_password_requests
|
from flask_security.utils import capture_reset_password_requests, md5, string_types
|
||||||
|
|
||||||
from utils import authenticate, init_app_with_options, populate_data
|
from utils import authenticate, init_app_with_options, populate_data
|
||||||
|
|
||||||
@@ -170,3 +170,19 @@ def test_change_hash_type(app, sqlalchemy_datastore):
|
|||||||
|
|
||||||
response = client.post('/login', data=dict(email='matt@lp.com', password='password'))
|
response = client.post('/login', data=dict(email='matt@lp.com', password='password'))
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
|
|
||||||
|
|
||||||
|
def test_md5():
|
||||||
|
data = md5(b'hello')
|
||||||
|
assert isinstance(data, string_types)
|
||||||
|
data = md5(u'hellö')
|
||||||
|
assert isinstance(data, string_types)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.settings(password_salt=u'öööööööööööööööööööööööööööööööööö',
|
||||||
|
password_hash='bcrypt')
|
||||||
|
def test_password_unicode_password_salt(client):
|
||||||
|
response = authenticate(client)
|
||||||
|
assert response.status_code == 302
|
||||||
|
response = authenticate(client, follow_redirects=True)
|
||||||
|
assert b'Hello matt@lp.com' in response.data
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def test_recoverable_flag(app, client, get_message):
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
})
|
})
|
||||||
assert response.headers['Content-Type'] == 'application/json'
|
assert response.headers['Content-Type'] == 'application/json'
|
||||||
assert 'user' in response.jdata['response']
|
assert 'user' not in response.jdata['response']
|
||||||
|
|
||||||
logout(client)
|
logout(client)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user