Skip to content

Commit

Permalink
Moved some UserManager methods into TokenManager, PasswordManager and…
Browse files Browse the repository at this point in the history
… EmailManager classes.
  • Loading branch information
lingthio committed Aug 28, 2017
1 parent f08ad07 commit e3327ed
Show file tree
Hide file tree
Showing 22 changed files with 226 additions and 176 deletions.
8 changes: 4 additions & 4 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,18 @@ hash_password()
~~~~~~~~~~~~~~~
::

user_manager.hash_password(password)
user_manager.password_manager.hash_password(password)
# Returns hashed 'password' using the configured password hash
# Config settings: USER_PASSWORD_HASH_MODE = 'passlib'
# USER_PASSWORD_HASH = 'bcrypt'
# USER_PASSWORD_SALT = SECRET_KEY


verify_password()
~~~~~~~~~~~~~~~~~
verify_user_password()
~~~~~~~~~~~~~~~~~~~~~~
::

user_manager.verify_password(password, user)
user_manager.password_manager.verify_user_password(user, password)
# Returns True if 'password' matches the user's 'hashed password'
# Returns False otherwise.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Roles are defined by adding rows to the role table with a specific Role.name val

# Create 'user007' user with 'secret' and 'agent' roles
user1 = User(username='user007', email='[email protected]', is_enabled=True,
password=user_manager.hash_password('Password1'))
password=user_manager.password_manager.hash_password('Password1'))
role1 = Role(name='secret')
role2 = Role(name='agent')

Expand Down
8 changes: 4 additions & 4 deletions docs/source/customization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ Password hashing

To hash a password, Flask-User:

* calls ``user_manager.hash_password()``,
* calls ``user_manager.password_manager.hash_password()``,
* which calls ``user_manager.password_crypt_context``,
* which is initialized to ``CryptContext(schemes=[app.config['USER_PASSWORD_HASH']])``,
* where ``USER_PASSWORD_HASH = 'bcrypt'``.
Expand All @@ -369,7 +369,7 @@ Developers can customize the password hashing in the following ways:
**By changing the crypt_context**::

my_password_crypt_context = CryptContext(
schemes=['bcrypt', 'sha512_crypt', 'pbkdf2_sha512', 'plaintext'])
schemes=['bcrypt', 'sha512_crypt', 'pbkdf2_sha512'])
user_manager = UserManager(db_adapter, app,
password_crypt_context=my_password_crypt_context)

Expand All @@ -379,8 +379,8 @@ Developers can customize the password hashing in the following ways:
def hash_password(self, password):
return self.password

def verify_password(self, password, user)
return self.hash_password(password) == self.get_password(user)
def verify_user_password(self, user, password)
return self.password_manager.hash_password(password) == self.get_password(user)

**Backward compatibility with Flask-Security**

Expand Down
104 changes: 77 additions & 27 deletions docs/source/porting.rst
Original file line number Diff line number Diff line change
@@ -1,44 +1,94 @@
================================
Porting Flask-User v0.6 to v0.9+
================================
=====================
Porting v0.6 to v1.0+
=====================

Flask-User v0.9 breaks backwards compatibility with v0.6 in order to support custom UserManager subclasses.
With Flask-User v0.1 through v0.6 we gained insights for an improved Flask-User API,
but we were unable to implement these improvements due to backwards compatibility.

UserManager() and init_app() parameter order
--------------------------------------------
Flask-User v0.9 changed the parameter order for UserManager() and init_app()::
With Flask-User v1.0, we decided to add these improvements at the cost of breaking backward compatibility.

user_manager = UserManager(app, db_adapter) # v0.9 style call
This page describes describes the changes required to make a Flask-User v0.6 application
work with Flask-User v1.0.

For backward compatibility reasons, the v0.6 parameter order is also supported, but not recommended::
UserManager() setup
-------------------
We simplified the Flask-User setup by removing the need to specify the db_adapter explicitly.

user_manager = UserManager(db_adapter, app) # v0.6 style call
Flask-User v0.6::

The v0.6 style will be suppored in v0.9 and v1.0, but will be obsoleted in the future.
from flask_sqlalchemy import SQLAlchemy
from flask_user import UserManager, SQLAlchemyAdapter

# Setup SQLAlchemy
db = SQLAlchemy(app)

verify_password() parameter order
---------------------------------
Flask-User v0.9 changed the parameter order for verify_password::
# Setup Flask-User
db_adapter = SQLAlchemyAdapter(db, User, UserEmailClass=UserEmail)
user_manager = UserManager(db_adapter, app)

verify_password(user, password) # v0.9 style call
Flask-User v1.0::

For backward compatibility reasons, the v0.6 parameter order is also supported, but not recommended::
from flask_sqlalchemy import SQLAlchemy
from flask_user import UserManager # No need for SQLAlchemyAdapter

verify_password(password, user) # v0.6 style call
# Setup SQLAlchemy
db = SQLAlchemy(app)

The v0.6 style will be suppored in v0.9 and v1.0, but will be obsoleted in the future.
# Setup Flask-User
user_manager = UserManager(app, db, User, UserEmailClass=UserEmail)

The `db` parameter can be any Database instance (for example `SQLAlchemy()` or a `MongoAlchemy()`) and the
appropriate DbAdapter will be configured internally.

Config settings
---------------
USER_ENABLE_EMAIL: The default is now 'False'. Set this to True if emails are used.

Optional: It is now recommended to use the CustomUserManager.customize() method to configure Flask-User settings::
PasswordManager() changes
-------------------------
Password related methods have been moved from the UserManager class to a separate PasswordManager class,
accessible through the UserManager.password_manager attribute.

We renamed `verify_password()` to `verify_user_password()` to better reflect what this method does.

We changed the `verify_user_password()` parameter order to be consistent with the parameter order of `update_user_hashed_password()`.

We renamed `update_password()` to `update_user_hashed_password()` to better reflect what this method does.

we renamed the `PASSWORD_HASH` setting to `PASSWORD_HASH_SCHEME` to better reflect what this setting means.

We removed the hash scheme option `"plaintext"` for security reasons.

Flask-User v0.6::

verify_password(password, user) # v0.6 parameter order
update_password(user, hashed_password)

Flask-User v1.0::

password_manager.verify_user_password(user, password) # v0.6 parameter order
password_manager.update_user_hashed_password(user, hashed_password)

As a courtesy, we allow both `verify_user_password()` parameter orders in v1.0, but a warning will
be issued and the v0.6 style will be obsoleted in the future.


EmailManager() changes
----------------------
Email related methods have been moved from the UserManager class to a separate EmailManager class,
accessible through the UserManager.email_manager attribute.


TokenManager() changes
----------------------
The v0.6 `_encode_id()` and `_decode_id()` assumed that IDs were limited to 16 digits.

This limitation has been removed in v1.0, to support Mongo ObjectIDs.

As a result, the generated tokens are different, which will affect two areas:

- v0.6 user-sessions that were stored in a browser cookie, are no longer valid in v1.0 and the user will be
required to login again.

- v0.6 password tokens that were sent in password reset emails are no longer valid in v1.0
and the user will have to issue a new forgot-password email request.
This effect is mitigated by the fact that these tokens are meant to expire relatively quickly.

# Define CustomUserManager subclass
class CustomUserManager(UserManager):

# Customize settings
def customize(self, app):
self.ENABLE_EMAIL = True # Note that it's 'ENABLE_EMAIL' and not 'USER_ENABLE_EMAIL'
8 changes: 4 additions & 4 deletions docs/source/recipes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,22 @@ See also: :doc:`signals`

Hashing Passwords
-----------------
If you want to populate your database with User records with hashed passwords use ``user_manager.hash_password()``:
If you want to populate your database with User records with hashed passwords use ``user_manager.password_manager.hash_password()``:

::

user = User(
email='[email protected]',
password=user_manager.hash_password('Password1'),
password=user_manager.password_manager.hash_password('Password1'),
)
db.session.add(user)
db.session.commit()

You can verify a password with ``user_manager.verify_password()``:
You can verify a password with ``user_manager.password_manager.verify_user_password()``:

::

does_match = user_manager.verify_password(password, user)
does_match = user_manager.password_manager.verify_user_password(user, password)

Account Tracking
----------------
Expand Down
2 changes: 1 addition & 1 deletion example_apps/roles_required_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class UserRoles(db.Model):
# Create 'user007' user with 'secret' and 'agent' roles
if not User.query.filter(User.username=='user007').first():
user1 = User(username='user007',
password=user_manager.hash_password('password'))
password=user_manager.password_manager.hash_password('password'))
user1.roles.append(Role(name='secret'))
user1.roles.append(Role(name='agent'))
db.session.add(user1)
Expand Down
2 changes: 1 addition & 1 deletion example_apps/user_auth_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class UserRoles(db.Model):
user1 = User(email='[email protected]', first_name='James', last_name='Bond')
db.session.add(user1)
user_auth1 = UserAuth(user=user1, username='user007',
password=user_manager.hash_password('Password1')
password=user_manager.password_manager.hash_password('Password1')
)
db.session.add(user_auth1)
user1.roles.append(Role(name='secret'))
Expand Down
26 changes: 17 additions & 9 deletions fabfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,12 @@ def cov():
local('py.test --cov flask_user --cov-report term-missing --cov-config flask_user/tests/.coveragerc flask_user/tests/')

@task
def babel_update():
def update_babel():
local('pybabel extract -F flask_user/translations/babel.cfg -k lazy_gettext -c NOTE -o flask_user/translations/flask_user.pot flask_user flask_user')
for code in ('de', 'en', 'es', 'fa', 'fi', 'fr', 'it', 'nl', 'ru', 'sv', 'tr', 'zh'):
local('pybabel update -i flask_user/translations/flask_user.pot --domain=flask_user --output-dir flask_user/translations -l '+code)
local('pybabel compile -f --domain=flask_user --directory flask_user/translations')

@task
def babel_init():
local('pybabel extract -F flask_user/translations/babel.cfg -k lazy_gettext -c NOTE -o flask_user/translations/flask_user.pot flask_user flask_user')
for code in ('de', 'en', 'es', 'fa', 'fi', 'fr', 'it', 'nl', 'ru', 'sv', 'tr', 'zh'):
local('pybabel init -i flask_user/translations/flask_user.pot --domain=flask_user --output-dir flask_user/translations -l '+code)
local('pybabel compile -f --domain=flask_user --directory flask_user/translations')

@task
def docs():
local('cp example_apps/*_app.py docs/source/includes/.')
Expand All @@ -49,4 +42,19 @@ def rebuild_docs():
@task
def upload_to_pypi():
local('python setup.py sdist')
local('twine upload dist/*')
local('twine upload dist/*')

# PyEnv: https://gist.github.com/Bouke/11261620
# PyEnv and Tox: https://www.holger-peters.de/using-pyenv-and-tox.html
# Available Python versions: pyenv install --list
@task
def setup_tox():
versions_str = '2.6.9 2.7.13 3.3.6 3.4.6 3.5.3 3.6.2'
versions = versions_str.split()
for version in versions:
local('pyenv install --skip-existing '+version)
local('pyenv global '+versions_str)

@task
def tox():
local('tox')
Loading

0 comments on commit e3327ed

Please sign in to comment.