mirror of
https://github.com/wassname/flask-security.git
synced 2026-07-03 17:10:25 +08:00
Merge branch 'develop'
This commit is contained in:
@@ -4,6 +4,7 @@ python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "pypy"
|
||||
|
||||
install:
|
||||
|
||||
@@ -3,6 +3,18 @@ Flask-Security Changelog
|
||||
|
||||
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
|
||||
-------------
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from itsdangerous import URLSafeTimedSerializer
|
||||
from passlib.context import CryptContext
|
||||
from werkzeug.datastructures import ImmutableList
|
||||
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 .views import create_blueprint
|
||||
@@ -193,7 +194,7 @@ def _token_loader(token):
|
||||
try:
|
||||
data = _security.remember_token_serializer.loads(token)
|
||||
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
|
||||
except:
|
||||
pass
|
||||
|
||||
+16
-4
@@ -99,13 +99,15 @@ def get_hmac(password):
|
||||
|
||||
:param password: The password to sign
|
||||
"""
|
||||
if _security.password_salt is None:
|
||||
salt = _security.password_salt
|
||||
|
||||
if 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.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())
|
||||
|
||||
|
||||
@@ -149,8 +151,18 @@ def encrypt_password(password):
|
||||
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):
|
||||
return hashlib.md5(data.encode('ascii')).hexdigest()
|
||||
return hashlib.md5(encode_string(data)).hexdigest()
|
||||
|
||||
|
||||
def do_flash(message, category=None):
|
||||
@@ -196,7 +208,7 @@ def url_for_security(endpoint, **values):
|
||||
|
||||
|
||||
def validate_redirect_url(url):
|
||||
if url is None:
|
||||
if url is None or url.strip() == '':
|
||||
return False
|
||||
url_next = urlsplit(url)
|
||||
url_base = urlsplit(request.host_url)
|
||||
|
||||
@@ -34,7 +34,7 @@ _security = LocalProxy(lambda: current_app.extensions['security'])
|
||||
_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
|
||||
|
||||
if has_errors:
|
||||
@@ -42,7 +42,9 @@ def _render_json(form, include_auth_token=False):
|
||||
response = dict(errors=form.errors)
|
||||
else:
|
||||
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:
|
||||
token = form.user.get_auth_token()
|
||||
response['user']['authentication_token'] = token
|
||||
@@ -78,7 +80,7 @@ def login():
|
||||
return redirect(get_post_login_redirect(form.next.data))
|
||||
|
||||
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'),
|
||||
login_user_form=form,
|
||||
@@ -121,7 +123,7 @@ def register():
|
||||
|
||||
if not request.json:
|
||||
return redirect(get_post_register_redirect())
|
||||
return _render_json(form, True)
|
||||
return _render_json(form, include_auth_token=True)
|
||||
|
||||
if request.json:
|
||||
return _render_json(form)
|
||||
@@ -247,7 +249,7 @@ def forgot_password():
|
||||
do_flash(*get_message('PASSWORD_RESET_REQUEST', email=form.user.email))
|
||||
|
||||
if request.json:
|
||||
return _render_json(form)
|
||||
return _render_json(form, include_user=False)
|
||||
|
||||
return _security.render_template(config_value('FORGOT_PASSWORD_TEMPLATE'),
|
||||
forgot_password_form=form,
|
||||
|
||||
@@ -23,11 +23,13 @@ def test_view_configuration(client):
|
||||
response = client.get('/custom_login')
|
||||
assert b"<h1>Login</h1>" in response.data
|
||||
|
||||
response = authenticate(client, endpoint='/custom_login', follow_redirects=True)
|
||||
assert b'Post Login' in response.data
|
||||
response = authenticate(client, endpoint='/custom_login')
|
||||
assert 'location' in response.headers
|
||||
assert response.headers['Location'] == 'http://localhost/post_login'
|
||||
|
||||
response = logout(client, endpoint='/custom_logout', follow_redirects=True)
|
||||
assert b'Post Logout' in response.data
|
||||
response = logout(client, endpoint='/custom_logout')
|
||||
assert 'location' in response.headers
|
||||
assert response.headers['Location'] == 'http://localhost/post_logout'
|
||||
|
||||
response = client.get('/http', headers={
|
||||
'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, \
|
||||
SendConfirmationForm, PasswordlessLoginForm, ForgotPasswordForm, ResetPasswordForm, \
|
||||
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
|
||||
|
||||
@@ -170,3 +170,19 @@ def test_change_hash_type(app, sqlalchemy_datastore):
|
||||
|
||||
response = client.post('/login', data=dict(email='matt@lp.com', password='password'))
|
||||
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'
|
||||
})
|
||||
assert response.headers['Content-Type'] == 'application/json'
|
||||
assert 'user' in response.jdata['response']
|
||||
assert 'user' not in response.jdata['response']
|
||||
|
||||
logout(client)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user