mirror of
https://github.com/wassname/flask-security.git
synced 2026-06-30 16:40:04 +08:00
Refactor login_user and logout_user to be a utility method, thus it can be reused if necessary
This commit is contained in:
+7
-7
@@ -34,13 +34,13 @@ your Flask application. They include:
|
||||
Many of these features are made possible by integrating various Flask extensions
|
||||
and libraries. They include:
|
||||
|
||||
1. Flask-Login
|
||||
2. Flask-Mail
|
||||
3. Flask-Principal
|
||||
4. Flask-Script
|
||||
5. Flask-WTF
|
||||
6. itsdangerous
|
||||
7. passlib
|
||||
1. `Flask-Login <http://packages.python.org/Flask-Login/>`_
|
||||
2. `Flask-Mail <http://packages.python.org/Flask-Mail/>`_
|
||||
3. `Flask-Principal <http://packages.python.org/Flask-Principal/>`_
|
||||
4. `Flask-Script <http://packages.python.org/Flask-Script/>`_
|
||||
5. `Flask-WTF <http://packages.python.org/Flask-Mail/>`_
|
||||
6. `itsdangerous <http://packages.python.org/itsdangerous/>`_
|
||||
7. `passlib <http://packages.python.org/passlib/>`_
|
||||
|
||||
Additionally, it assumes you'll be using a common library for your database
|
||||
connections and model definitions. Flask-Security thus supports SQLAlchemy and
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Flask-Security
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module was generated by Flask-Security to give developers greater
|
||||
control over the various security mechanisms. For more information about
|
||||
using this feature see:
|
||||
|
||||
TODO: Documentation URL
|
||||
"""
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from flask import current_app as app, redirect, request, session, \
|
||||
render_template, jsonify, Blueprint
|
||||
from flask.ext.login import login_user, logout_user
|
||||
from flask.ext.principal import Identity, AnonymousIdentity, identity_changed
|
||||
from werkzeug.datastructures import MultiDict
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from flask_security.confirmable import confirm_by_token, reset_confirmation_token
|
||||
from flask_security.decorators import login_required
|
||||
from flask_security.exceptions import ConfirmationError, BadCredentialsError, \
|
||||
ResetPasswordError
|
||||
from flask_security.forms import LoginForm, RegisterForm, ForgotPasswordForm, \
|
||||
ResetPasswordForm, ResendConfirmationForm
|
||||
from flask_security.recoverable import reset_by_token, \
|
||||
reset_password_reset_token
|
||||
from flask_security.signals import user_registered
|
||||
from flask_security.tokens import generate_authentication_token
|
||||
from flask_security.utils import get_url, get_post_login_redirect, do_flash, \
|
||||
get_remember_token, get_message, config_value
|
||||
|
||||
|
||||
# Convenient references
|
||||
_security = LocalProxy(lambda: app.security)
|
||||
|
||||
_datastore = LocalProxy(lambda: app.security.datastore)
|
||||
|
||||
_logger = LocalProxy(lambda: app.logger)
|
||||
|
||||
|
||||
def _do_login(user, remember=True):
|
||||
"""Performs the login and sends the appropriate signal."""
|
||||
|
||||
if not login_user(user, remember):
|
||||
return False
|
||||
|
||||
if user.authentication_token is None:
|
||||
user.authentication_token = generate_authentication_token(user)
|
||||
|
||||
if remember:
|
||||
user.remember_token = get_remember_token(user.email, user.password)
|
||||
|
||||
if _security.trackable:
|
||||
old_current, new_current = user.current_login_at, datetime.utcnow()
|
||||
user.last_login_at = old_current or new_current
|
||||
user.current_login_at = new_current
|
||||
|
||||
old_current, new_current = user.current_login_ip, request.remote_addr
|
||||
user.last_login_ip = old_current or new_current
|
||||
user.current_login_ip = new_current
|
||||
|
||||
user.login_count = user.login_count + 1 if user.login_count else 0
|
||||
|
||||
_datastore._save_model(user)
|
||||
|
||||
identity_changed.send(app._get_current_object(),
|
||||
identity=Identity(user.id))
|
||||
|
||||
_logger.debug('User %s logged in' % user)
|
||||
return True
|
||||
|
||||
|
||||
def _json_auth_ok(user):
|
||||
return jsonify({
|
||||
"meta": {
|
||||
"code": 200
|
||||
},
|
||||
"response": {
|
||||
"user": {
|
||||
"id": str(user.id),
|
||||
"authentication_token": user.authentication_token
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
def _json_auth_error(msg):
|
||||
resp = jsonify({
|
||||
"meta": {
|
||||
"code": 400
|
||||
},
|
||||
"response": {
|
||||
"error": msg
|
||||
}
|
||||
})
|
||||
resp.status_code = 400
|
||||
return resp
|
||||
|
||||
|
||||
def authenticate():
|
||||
"""View function which handles an authentication request."""
|
||||
|
||||
form = LoginForm(MultiDict(request.json) if request.json else request.form)
|
||||
|
||||
try:
|
||||
user = _security.auth_provider.authenticate(form)
|
||||
|
||||
if _do_login(user, remember=form.remember.data):
|
||||
if request.json:
|
||||
return _json_auth_ok(user)
|
||||
|
||||
return redirect(get_post_login_redirect())
|
||||
|
||||
raise BadCredentialsError('Account is disabled')
|
||||
|
||||
except BadCredentialsError, e:
|
||||
msg = str(e)
|
||||
|
||||
_logger.debug('Unsuccessful authentication attempt: %s' % msg)
|
||||
|
||||
if request.json:
|
||||
return _json_auth_error(msg)
|
||||
|
||||
do_flash(msg, 'error')
|
||||
|
||||
return redirect(request.referrer or _security.login_manager.login_view)
|
||||
|
||||
|
||||
def logout():
|
||||
"""View function which handles a logout request."""
|
||||
|
||||
for key in ('identity.name', 'identity.auth_type'):
|
||||
session.pop(key, None)
|
||||
|
||||
identity_changed.send(app._get_current_object(),
|
||||
identity=AnonymousIdentity())
|
||||
|
||||
logout_user()
|
||||
_logger.debug('User logged out')
|
||||
|
||||
return redirect(request.args.get('next', None) or
|
||||
_security.post_logout_view)
|
||||
|
||||
|
||||
def register_user():
|
||||
"""View function which handles a registration request."""
|
||||
|
||||
form = RegisterForm(csrf_enabled=not app.testing)
|
||||
|
||||
if form.validate_on_submit():
|
||||
# Create user
|
||||
u = _datastore.create_user(**form.to_dict())
|
||||
|
||||
# Send confirmation instructions if necessary
|
||||
t = reset_confirmation_token(u) if _security.confirmable else None
|
||||
|
||||
data = dict(user=u, confirm_token=t)
|
||||
user_registered.send(data, app=app._get_current_object())
|
||||
|
||||
_logger.debug('User %s registered' % u)
|
||||
|
||||
# Login the user if allowed
|
||||
if not _security.confirmable or _security.login_without_confirmation:
|
||||
_do_login(u)
|
||||
|
||||
return redirect(_security.post_register_view or
|
||||
_security.post_login_view)
|
||||
|
||||
return render_template('security/registrations/new.html',
|
||||
register_user_form=form)
|
||||
|
||||
|
||||
def send_confirmation():
|
||||
form = ResendConfirmationForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
user = _datastore.find_user(email=form.email.data)
|
||||
|
||||
reset_confirmation_token(user)
|
||||
|
||||
_logger.debug('%s request confirmation instructions' % user)
|
||||
|
||||
msg, cat = get_message('CONFIRMATION_REQUEST', email=user.email)
|
||||
|
||||
do_flash(msg, cat)
|
||||
|
||||
else:
|
||||
for key, value in form.errors.items():
|
||||
do_flash(value[0], 'error')
|
||||
|
||||
return render_template('security/confirmations/new.html',
|
||||
reset_confirmation_form=form)
|
||||
|
||||
|
||||
def confirm_account(token):
|
||||
"""View function which handles a account confirmation request."""
|
||||
try:
|
||||
user = confirm_by_token(token)
|
||||
_logger.debug('%s confirmed their account' % user)
|
||||
|
||||
except ConfirmationError, e:
|
||||
msg, cat = str(e), 'error'
|
||||
|
||||
_logger.debug('Confirmation error: ' + msg)
|
||||
|
||||
if e.user:
|
||||
reset_confirmation_token(e.user)
|
||||
|
||||
msg, cat = get_message('CONFIRMATION_EXPIRED',
|
||||
within=_security.confirm_email_within,
|
||||
email=e.user.email)
|
||||
|
||||
do_flash(msg, cat)
|
||||
|
||||
return redirect(get_url(_security.confirm_error_view))
|
||||
|
||||
do_flash(get_message('ACCOUNT_CONFIRMED'))
|
||||
|
||||
return redirect(_security.post_confirm_view or _security.post_login_view)
|
||||
|
||||
|
||||
def forgot_password():
|
||||
"""View function that handles a forgotten password request."""
|
||||
|
||||
form = ForgotPasswordForm(csrf_enabled=not app.testing)
|
||||
|
||||
if form.validate_on_submit():
|
||||
user = _datastore.find_user(**form.to_dict())
|
||||
|
||||
reset_password_reset_token(user)
|
||||
|
||||
_logger.debug('%s requested to reset their password' % user)
|
||||
|
||||
msg, cat = get_message('PASSWORD_RESET_REQUEST', email=user.email)
|
||||
|
||||
do_flash(msg, cat)
|
||||
|
||||
return redirect(_security.post_forgot_view)
|
||||
|
||||
else:
|
||||
_logger.debug('A reset password request was made for %s but '
|
||||
'that email does not exist.' % form.email.data)
|
||||
|
||||
for key, value in form.errors.items():
|
||||
do_flash(value[0], 'error')
|
||||
|
||||
return render_template('security/passwords/new.html',
|
||||
forgot_password_form=form)
|
||||
|
||||
|
||||
def reset_password(token):
|
||||
"""View function that handles a reset password request."""
|
||||
|
||||
form = ResetPasswordForm(csrf_enabled=not app.testing)
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
user = reset_by_token(token=token, **form.to_dict())
|
||||
_logger.debug('%s reset their password' % user)
|
||||
|
||||
except ResetPasswordError, e:
|
||||
msg, cat = str(e), 'error'
|
||||
|
||||
_logger.debug('Password reset error: ' + msg)
|
||||
|
||||
if e.user:
|
||||
reset_password_reset_token(e.user)
|
||||
|
||||
msg, cat = get_message('PASSWORD_RESET_EXPIRED',
|
||||
within=_security.reset_password_within,
|
||||
email=e.user.email)
|
||||
|
||||
do_flash(msg, cat)
|
||||
|
||||
return render_template('security/passwords/edit.html',
|
||||
reset_password_form=form,
|
||||
password_reset_token=token)
|
||||
|
||||
|
||||
def create_blueprint(app, name, import_name, **kwargs):
|
||||
bp = Blueprint(name, import_name, **kwargs)
|
||||
|
||||
bp.route(config_value('AUTH_URL', app=app),
|
||||
methods=['POST'],
|
||||
endpoint='authenticate')(authenticate)
|
||||
|
||||
bp.route(config_value('LOGOUT_URL', app=app),
|
||||
endpoint='logout')(login_required(logout))
|
||||
|
||||
if config_value('REGISTERABLE', app=app):
|
||||
bp.route(config_value('REGISTER_URL', app=app),
|
||||
methods=['GET', 'POST'],
|
||||
endpoint='register')(register_user)
|
||||
|
||||
if config_value('RECOVERABLE', app=app):
|
||||
bp.route(config_value('RESET_URL', app=app),
|
||||
methods=['GET', 'POST'],
|
||||
endpoint='forgot_password')(forgot_password)
|
||||
bp.route(config_value('RESET_URL', app=app) + '/<token>',
|
||||
methods=['GET', 'POST'],
|
||||
endpoint='reset_password')(reset_password)
|
||||
|
||||
if config_value('CONFIRMABLE', app=app):
|
||||
bp.route(config_value('CONFIRM_URL', app=app),
|
||||
methods=['GET', 'POST'],
|
||||
endpoint='send_confirmation')(send_confirmation)
|
||||
bp.route(config_value('CONFIRM_URL', app=app) + '/<token>',
|
||||
methods=['GET', 'POST'],
|
||||
endpoint='confirm_account')(confirm_account)
|
||||
|
||||
return bp
|
||||
@@ -10,8 +10,6 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask.ext.login import login_user, logout_user
|
||||
|
||||
from .core import Security, RoleMixin, UserMixin, AnonymousUser, \
|
||||
AuthenticationProvider, current_user
|
||||
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore
|
||||
@@ -22,3 +20,4 @@ from .forms import ForgotPasswordForm, LoginForm, RegisterForm, \
|
||||
from .signals import confirm_instructions_sent, password_reset, \
|
||||
password_reset_requested, reset_instructions_sent, user_confirmed, \
|
||||
user_registered
|
||||
from .utils import login_user, logout_user
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.ext.security.tokens
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Flask-Security tokens module
|
||||
|
||||
:copyright: (c) 2012 by Matt Wright.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask import current_app as app
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from .utils import md5
|
||||
|
||||
|
||||
# Convenient references
|
||||
_security = LocalProxy(lambda: app.extensions['security'])
|
||||
|
||||
_datastore = LocalProxy(lambda: _security.datastore)
|
||||
|
||||
|
||||
def generate_authentication_token(user):
|
||||
"""Generates a unique authentication token for the specified user.
|
||||
|
||||
:param user: The user to work with
|
||||
"""
|
||||
data = [str(user.id), md5(user.email)]
|
||||
return _security.token_auth_serializer.dumps(data)
|
||||
|
||||
|
||||
def reset_authentication_token(user):
|
||||
"""Resets a user's authentication token and returns the new token value.
|
||||
|
||||
:param user: The user to work with
|
||||
"""
|
||||
token = generate_authentication_token(user)
|
||||
user.authentication_token = token
|
||||
_datastore._save_model(user)
|
||||
return token
|
||||
|
||||
|
||||
def ensure_authentication_token(user):
|
||||
"""Ensures that a user has an authentication token. If the user has an
|
||||
authentication token already, nothing is performed.
|
||||
|
||||
:param user: The user to work with
|
||||
"""
|
||||
if not user.authentication_token:
|
||||
return reset_authentication_token(user)
|
||||
+60
-1
@@ -17,7 +17,9 @@ from contextlib import contextmanager
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from flask import url_for, flash, current_app, request, session, render_template
|
||||
from flask.ext.login import make_secure_token
|
||||
from flask.ext.login import make_secure_token, login_user as _login_user, \
|
||||
logout_user as _logout_user
|
||||
from flask.ext.principal import Identity, AnonymousIdentity, identity_changed
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from .signals import user_registered, password_reset_requested
|
||||
@@ -26,8 +28,56 @@ from .signals import user_registered, password_reset_requested
|
||||
# Convenient references
|
||||
_security = LocalProxy(lambda: current_app.extensions['security'])
|
||||
|
||||
_datastore = LocalProxy(lambda: _security.datastore)
|
||||
|
||||
_pwd_context = LocalProxy(lambda: _security.pwd_context)
|
||||
|
||||
_logger = LocalProxy(lambda: current_app.logger)
|
||||
|
||||
|
||||
def login_user(user, remember=True):
|
||||
"""Performs the login and sends the appropriate signal."""
|
||||
|
||||
if not _login_user(user, remember):
|
||||
return False
|
||||
|
||||
if user.authentication_token is None:
|
||||
from .tokens import generate_authentication_token
|
||||
|
||||
user.authentication_token = generate_authentication_token(user)
|
||||
|
||||
if remember:
|
||||
user.remember_token = get_remember_token(user.email, user.password)
|
||||
|
||||
if _security.trackable:
|
||||
old_current, new_current = user.current_login_at, datetime.utcnow()
|
||||
user.last_login_at = old_current or new_current
|
||||
user.current_login_at = new_current
|
||||
|
||||
old_current, new_current = user.current_login_ip, request.remote_addr
|
||||
user.last_login_ip = old_current or new_current
|
||||
user.current_login_ip = new_current
|
||||
|
||||
user.login_count = user.login_count + 1 if user.login_count else 0
|
||||
|
||||
_datastore._save_model(user)
|
||||
|
||||
identity_changed.send(current_app._get_current_object(),
|
||||
identity=Identity(user.id))
|
||||
|
||||
_logger.debug('User %s logged in' % user)
|
||||
return True
|
||||
|
||||
|
||||
def logout_user():
|
||||
for key in ('identity.name', 'identity.auth_type'):
|
||||
session.pop(key, None)
|
||||
|
||||
identity_changed.send(current_app._get_current_object(),
|
||||
identity=AnonymousIdentity())
|
||||
|
||||
_logout_user()
|
||||
|
||||
|
||||
def get_hmac(msg, salt=None, digestmod=None):
|
||||
digestmod = digestmod or hashlib.sha512
|
||||
@@ -44,6 +94,15 @@ def encrypt_password(password, salt=None, use_hmac=False):
|
||||
return _pwd_context.encrypt(hmac_value)
|
||||
|
||||
|
||||
def generate_authentication_token(user):
|
||||
"""Generates a unique authentication token for the specified user.
|
||||
|
||||
:param user: The user to work with
|
||||
"""
|
||||
data = [str(user.id), md5(user.email)]
|
||||
return _security.token_auth_serializer.dumps(data)
|
||||
|
||||
|
||||
def md5(data):
|
||||
return hashlib.md5(data).hexdigest()
|
||||
|
||||
|
||||
+5
-46
@@ -9,12 +9,9 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from flask import current_app as app, redirect, request, session, \
|
||||
render_template, jsonify, Blueprint
|
||||
from flask.ext.login import login_user, logout_user
|
||||
from flask.ext.principal import Identity, AnonymousIdentity, identity_changed
|
||||
from flask.ext.principal import AnonymousIdentity, identity_changed
|
||||
from werkzeug.datastructures import MultiDict
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
@@ -27,9 +24,8 @@ from flask_security.forms import LoginForm, RegisterForm, ForgotPasswordForm, \
|
||||
from flask_security.recoverable import reset_by_token, \
|
||||
reset_password_reset_token
|
||||
from flask_security.signals import user_registered
|
||||
from flask_security.tokens import generate_authentication_token
|
||||
from flask_security.utils import get_url, get_post_login_redirect, do_flash, \
|
||||
get_remember_token, get_message, config_value
|
||||
get_message, config_value, login_user, logout_user
|
||||
|
||||
|
||||
# Convenient references
|
||||
@@ -40,38 +36,6 @@ _datastore = LocalProxy(lambda: _security.datastore)
|
||||
_logger = LocalProxy(lambda: app.logger)
|
||||
|
||||
|
||||
def _do_login(user, remember=True):
|
||||
"""Performs the login and sends the appropriate signal."""
|
||||
|
||||
if not login_user(user, remember):
|
||||
return False
|
||||
|
||||
if user.authentication_token is None:
|
||||
user.authentication_token = generate_authentication_token(user)
|
||||
|
||||
if remember:
|
||||
user.remember_token = get_remember_token(user.email, user.password)
|
||||
|
||||
if _security.trackable:
|
||||
old_current, new_current = user.current_login_at, datetime.utcnow()
|
||||
user.last_login_at = old_current or new_current
|
||||
user.current_login_at = new_current
|
||||
|
||||
old_current, new_current = user.current_login_ip, request.remote_addr
|
||||
user.last_login_ip = old_current or new_current
|
||||
user.current_login_ip = new_current
|
||||
|
||||
user.login_count = user.login_count + 1 if user.login_count else 0
|
||||
|
||||
_datastore._save_model(user)
|
||||
|
||||
identity_changed.send(app._get_current_object(),
|
||||
identity=Identity(user.id))
|
||||
|
||||
_logger.debug('User %s logged in' % user)
|
||||
return True
|
||||
|
||||
|
||||
def _json_auth_ok(user):
|
||||
return jsonify({
|
||||
"meta": {
|
||||
@@ -107,7 +71,7 @@ def authenticate():
|
||||
try:
|
||||
user = _security.auth_provider.authenticate(form)
|
||||
|
||||
if _do_login(user, remember=form.remember.data):
|
||||
if login_user(user, remember=form.remember.data):
|
||||
if request.json:
|
||||
return _json_auth_ok(user)
|
||||
|
||||
@@ -131,13 +95,8 @@ def authenticate():
|
||||
def logout():
|
||||
"""View function which handles a logout request."""
|
||||
|
||||
for key in ('identity.name', 'identity.auth_type'):
|
||||
session.pop(key, None)
|
||||
|
||||
identity_changed.send(app._get_current_object(),
|
||||
identity=AnonymousIdentity())
|
||||
|
||||
logout_user()
|
||||
|
||||
_logger.debug('User logged out')
|
||||
|
||||
return redirect(request.args.get('next', None) or
|
||||
@@ -163,7 +122,7 @@ def register_user():
|
||||
|
||||
# Login the user if allowed
|
||||
if not _security.confirmable or _security.login_without_confirmation:
|
||||
_do_login(u)
|
||||
login_user(u)
|
||||
|
||||
return redirect(_security.post_register_view or
|
||||
_security.post_login_view)
|
||||
|
||||
Reference in New Issue
Block a user