A bunch of adjustments to satisfy existing tests and even some new ones

This commit is contained in:
Matt Wright
2014-03-14 15:26:53 -04:00
parent e5111dbb0c
commit 510d1356a2
5 changed files with 77 additions and 64 deletions
+17 -19
View File
@@ -87,7 +87,18 @@ _default_config = {
'EMAIL_SUBJECT_PASSWORD_NOTICE': 'Your password has been reset',
'EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE': 'Your password has been changed',
'EMAIL_SUBJECT_PASSWORD_RESET': 'Password reset instructions',
'USER_IDENTITY_ATTRIBUTES': ['email']
'USER_IDENTITY_ATTRIBUTES': ['email'],
'PASSWORD_SCHEMES': [
'bcrypt',
'des_crypt',
'pbkdf2_sha256',
'pbkdf2_sha512',
'sha256_crypt',
'sha512_crypt',
# And always last one...
'plaintext'
],
'DEPRECATED_PASSWORD_SCHEMES': ['auto']
}
#: Default Flask-Security messages
@@ -162,17 +173,6 @@ _default_messages = {
'Please reauthenticate to access this page.', 'info'),
}
_allowed_password_hash_schemes = [
'bcrypt',
'des_crypt',
'pbkdf2_sha256',
'pbkdf2_sha512',
'sha256_crypt',
'sha512_crypt',
# And always last one...
'plaintext'
]
_default_forms = {
'login_form': LoginForm,
'confirm_register_form': ConfirmRegisterForm,
@@ -242,11 +242,12 @@ def _get_principal(app):
def _get_pwd_context(app):
pw_hash = cv('PASSWORD_HASH', app=app)
if pw_hash not in _allowed_password_hash_schemes:
allowed = (', '.join(_allowed_password_hash_schemes[:-1]) +
' and ' + _allowed_password_hash_schemes[-1])
schemes = cv('PASSWORD_SCHEMES', app=app)
deprecated = cv('DEPRECATED_PASSWORD_SCHEMES', app=app)
if pw_hash not in schemes:
allowed = (', '.join(schemes[:-1]) + ' and ' + schemes[-1])
raise ValueError("Invalid hash scheme %r. Allowed values are %s" % (pw_hash, allowed))
return CryptContext(schemes=_allowed_password_hash_schemes, default=pw_hash)
return CryptContext(schemes=schemes, default=pw_hash, deprecated=deprecated)
def _get_serializer(app, name):
@@ -345,9 +346,6 @@ class _SecurityState(object):
rv.update(fn())
return rv
def context_processor(self, fn):
self._add_ctx_processor(None, fn)
def forgot_password_context_processor(self, fn):
self._add_ctx_processor('forgot_password', fn)
+9 -7
View File
@@ -237,17 +237,18 @@ class MongoEngineUserDatastore(MongoEngineDatastore, UserDatastore):
query = QCombination(QCombination.AND, queries)
try:
return self.user_model.objects(query).first()
except ValidationError:
except ValidationError: # pragma: no cover
return None
def find_role(self, role):
return self.role_model.objects(name=role).first()
def add_role_to_user(self, user, role):
rv = super(MongoEngineUserDatastore, self).add_role_to_user(user, role)
if rv:
self.put(user)
return rv
# TODO: Not sure why this was added but tests pass without it
# def add_role_to_user(self, user, role):
# rv = super(MongoEngineUserDatastore, self).add_role_to_user(user, role)
# if rv:
# self.put(user)
# return rv
class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
@@ -295,6 +296,7 @@ class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
user = self.put(user)
for role in roles:
self.add_role_to_user(user, role)
self.put(user)
return user
def add_role_to_user(self, user, role):
@@ -309,7 +311,7 @@ class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
if result.count():
return False
else:
self.UserRole.create(user=user.id, role=role.id)
self.put(self.UserRole.create(user=user.id, role=role.id))
return True
def remove_role_from_user(self, user, role):
+8 -15
View File
@@ -10,12 +10,8 @@
"""
import inspect
try:
from urlparse import urlsplit
except ImportError:
from urllib.parse import urlsplit
from flask import request, current_app
from flask import request, current_app, flash
from flask_wtf import Form as BaseForm
from wtforms import TextField, PasswordField, validators, \
SubmitField, HiddenField, BooleanField, ValidationError, Field
@@ -23,7 +19,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, config_value
from .utils import verify_and_update_password, get_message, config_value, validate_redirect_url
# Convenient reference
_datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)
@@ -137,12 +133,10 @@ class NextFormMixin():
next = HiddenField()
def validate_next(self, field):
if field.data:
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])
if field.data and not validate_redirect_url(field.data):
field.data = ''
flash(*get_message('INVALID_REDIRECT'))
raise ValidationError(get_message('INVALID_REDIRECT')[0])
class RegisterFormMixin():
@@ -209,6 +203,8 @@ class LoginForm(Form, NextFormMixin):
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
if not self.next.data:
self.next.data = request.args.get('next', '')
self.remember.default = config_value('DEFAULT_REMEMBER_ME')
def validate(self):
@@ -275,9 +271,6 @@ class ChangePasswordForm(Form, PasswordFormMixin):
if not super(ChangePasswordForm, self).validate():
return False
if self.password.data.strip() == '':
self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0])
return False
if not verify_and_update_password(self.password.data, current_user):
self.password.errors.append(get_message('INVALID_PASSWORD')[0])
return False
+40 -18
View File
@@ -14,6 +14,11 @@ import hashlib
import hmac
import sys
try:
from urlparse import urlsplit
except ImportError: # pragma: no cover
from urllib.parse import urlsplit
from contextlib import contextmanager
from datetime import datetime, timedelta
@@ -36,11 +41,11 @@ _pwd_context = LocalProxy(lambda: _security.pwd_context)
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
text_type = str
string_types = str, # pragma: no cover, no flakes
text_type = str # pragma: no cover, no flakes
else:
string_types = basestring,
text_type = unicode
string_types = basestring, # pragma: no cover, no flakes
text_type = unicode # pragma: no cover, no flakes
def login_user(user, remember=None):
@@ -53,7 +58,7 @@ def login_user(user, remember=None):
if remember is None:
remember = config_value('DEFAULT_REMEMBER_ME')
if not _login_user(user, remember):
if not _login_user(user, remember): # pragma: no cover
return False
if _security.trackable:
@@ -119,13 +124,16 @@ def verify_and_update_password(password, user):
:param password: A plaintext password to verify
:param user: The user to verify against
"""
if _security.password_hash != 'plaintext':
print _pwd_context.default_scheme()
print password, user.password
if _pwd_context.identify(user.password) != 'plaintext':
password = get_hmac(password)
verified, new_password = _pwd_context.verify_and_update(password, user.password)
print verified, new_password
if verified and new_password:
user.password = new_password
_datastore.put(user)
return verified
@@ -186,14 +194,32 @@ def url_for_security(endpoint, **values):
return url_for(endpoint, **values)
def get_post_action_redirect(config_key):
return (get_url(request.args.get('next')) or
get_url(request.form.get('next')) or
find_redirect(config_key))
def validate_redirect_url(url):
try:
url_next = urlsplit(url)
except:
return False
url_base = urlsplit(request.host_url)
if url_next.netloc and url_next.netloc != url_base.netloc:
return False
return True
def get_post_login_redirect():
return get_post_action_redirect('SECURITY_POST_LOGIN_VIEW')
def get_post_action_redirect(config_key, declared=None):
urls = [
get_url(request.args.get('next')),
get_url(request.form.get('next')),
find_redirect(config_key)
]
if declared:
urls.append(declared)
for url in urls:
if validate_redirect_url(url):
return url
def get_post_login_redirect(declared=None):
return get_post_action_redirect('SECURITY_POST_LOGIN_VIEW', declared)
def get_post_register_redirect():
@@ -314,11 +340,7 @@ def get_token_status(token, serializer, max_age=None):
except SignatureExpired:
d, data = serializer.loads_unsafe(token)
expired = True
except BadSignature:
invalid = True
except TypeError:
invalid = True
except ValueError:
except (BadSignature, TypeError, ValueError):
invalid = True
if data:
+3 -5
View File
@@ -75,11 +75,8 @@ def login():
after_this_request(_commit)
if not request.json:
return redirect(get_post_login_redirect())
form.next.data = (get_url(request.args.get('next')) or
get_url(request.form.get('next')) or
'')
rv = get_post_login_redirect(form.next.data)
return redirect(rv)
if request.json:
return _render_json(form, True)
@@ -124,6 +121,7 @@ def register():
login_user(user)
if not request.json:
print('wtf')
return redirect(get_post_register_redirect())
return _render_json(form, True)