mirror of
https://github.com/wassname/flask-security.git
synced 2026-08-11 11:18:40 +08:00
A bunch of adjustments to satisfy existing tests and even some new ones
This commit is contained in:
+17
-19
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user