mirror of
https://github.com/wassname/flask-security.git
synced 2026-07-15 01:10:54 +08:00
Decent clean up. Get rid of AuthProvider class in favor of keeping it simple
This commit is contained in:
@@ -12,8 +12,7 @@
|
||||
|
||||
__version__ = '1.3.0-dev'
|
||||
|
||||
from .core import Security, RoleMixin, UserMixin, AnonymousUser, \
|
||||
AuthenticationProvider, current_user
|
||||
from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
|
||||
from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore
|
||||
from .decorators import auth_token_required, http_auth_required, \
|
||||
login_required, roles_accepted, roles_required
|
||||
|
||||
@@ -55,7 +55,7 @@ def generate_confirmation_token(user):
|
||||
|
||||
def requires_confirmation(user):
|
||||
"""Returns `True` if the user requires confirmation."""
|
||||
return user.confirmed_at == None if _security.confirmable else False
|
||||
return user.confirmed_at == None and _security.confirmable
|
||||
|
||||
|
||||
def confirm_by_token(token):
|
||||
|
||||
+1
-53
@@ -76,6 +76,7 @@ _default_messages = {
|
||||
'ALREADY_CONFIRMED': ('Your email has already been confirmed.', 'info'),
|
||||
'INVALID_CONFIRMATION_TOKEN': ('Invalid confirmation token.', 'error'),
|
||||
'ALREADY_CONFIRMED': ('This email has already been confirmed', 'info'),
|
||||
'PASSWORD_MISMATCH': ('Password does not match', 'error'),
|
||||
'PASSWORD_RESET_REQUEST': ('Instructions to reset your password have been sent to %(email)s.', 'info'),
|
||||
'PASSWORD_RESET_EXPIRED': ('You did not reset your password within %(within)s. New instructions have been sent to %(email)s.', 'error'),
|
||||
'INVALID_RESET_PASSWORD_TOKEN': ('Invalid reset password token.', 'error'),
|
||||
@@ -313,7 +314,6 @@ class Security(object):
|
||||
for key, value in [
|
||||
('app', app),
|
||||
('datastore', datastore),
|
||||
('auth_provider', AuthenticationProvider()),
|
||||
('login_manager', _get_login_manager(app)),
|
||||
('principal', _get_principal(app)),
|
||||
('pwd_context', _get_pwd_context(app)),
|
||||
@@ -331,55 +331,3 @@ class Security(object):
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._state, name, None)
|
||||
|
||||
|
||||
class AuthenticationProvider(object):
|
||||
"""The default authentication provider implementation."""
|
||||
def _get_user(self, username_or_email):
|
||||
datastore = _security.datastore
|
||||
|
||||
try:
|
||||
return datastore.find_user(email=username_or_email)
|
||||
except exceptions.UserNotFoundError:
|
||||
try:
|
||||
return datastore.find_user(username=username_or_email)
|
||||
except:
|
||||
raise exceptions.UserNotFoundError()
|
||||
|
||||
def authenticate(self, form):
|
||||
"""Processes an authentication request and returns a user instance if
|
||||
authentication is successful.
|
||||
|
||||
:param form: A populated WTForm instance that contains `email` and
|
||||
`password` form fields
|
||||
"""
|
||||
if not form.validate():
|
||||
if form.email.errors:
|
||||
raise exceptions.BadCredentialsError(form.email.errors[0])
|
||||
if form.password.errors:
|
||||
raise exceptions.BadCredentialsError(form.password.errors[0])
|
||||
|
||||
return self.do_authenticate(form.email.data, form.password.data)
|
||||
|
||||
def do_authenticate(self, username_or_email, password):
|
||||
"""Returns the authenticated user if authentication is successfull. If
|
||||
authentication fails an appropriate `AuthenticationError` is raised
|
||||
|
||||
:param username_or_email: The username or email address of the user
|
||||
:param password: The password supplied by the authentication request
|
||||
"""
|
||||
|
||||
try:
|
||||
user = self._get_user(username_or_email)
|
||||
except exceptions.UserNotFoundError:
|
||||
raise exceptions.BadCredentialsError('Specified user does not exist.')
|
||||
|
||||
if requires_confirmation(user):
|
||||
raise exceptions.ConfirmationError('Email requires confirmation.', user)
|
||||
|
||||
# compare passwords
|
||||
if verify_password(password, user.password):
|
||||
return user
|
||||
|
||||
# bad match
|
||||
raise exceptions.BadCredentialsError("Password does not match")
|
||||
|
||||
@@ -9,14 +9,8 @@
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask import current_app
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from . import exceptions
|
||||
|
||||
# Convenient references
|
||||
_security = LocalProxy(lambda: current_app.extensions['security'])
|
||||
|
||||
|
||||
class UserDatastore(object):
|
||||
"""Abstracted user datastore. Always extend this class and implement the
|
||||
@@ -177,42 +171,8 @@ class UserDatastore(object):
|
||||
class SQLAlchemyUserDatastore(UserDatastore):
|
||||
"""A SQLAlchemy datastore implementation for Flask-Security that assumes the
|
||||
use of the Flask-SQLAlchemy extension.
|
||||
|
||||
Example usage::
|
||||
|
||||
from flask import Flask
|
||||
from flask.ext.security import Security, SQLAlchemyUserDatastore
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'secret'
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = \
|
||||
'sqlite:////tmp/flask_security_example.sqlite'
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
roles_users = db.Table('roles_users',
|
||||
db.Column('user_id', db.Integer(), db.ForeignKey('role.id')),
|
||||
db.Column('role_id', db.Integer(), db.ForeignKey('user.id')))
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
name = db.Column(db.String(80), unique=True)
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
email = db.Column(db.String(255), unique=True)
|
||||
password = db.Column(db.String(120))
|
||||
first_name = db.Column(db.String(120))
|
||||
last_name = db.Column(db.String(120))
|
||||
active = db.Column(db.Boolean())
|
||||
created_at = db.Column(db.DateTime())
|
||||
modified_at = db.Column(db.DateTime())
|
||||
roles = db.relationship('Role', secondary=roles_users,
|
||||
backref=db.backref('users', lazy='dynamic'))
|
||||
|
||||
Security(app, SQLAlchemyUserDatastore(db, User, Role))
|
||||
"""
|
||||
|
||||
def _commit(self, *args, **kwargs):
|
||||
self.db.session.commit()
|
||||
|
||||
@@ -233,31 +193,6 @@ class SQLAlchemyUserDatastore(UserDatastore):
|
||||
class MongoEngineUserDatastore(UserDatastore):
|
||||
"""A MongoEngine datastore implementation for Flask-Security that assumes
|
||||
the use of the Flask-MongoEngine extension.
|
||||
|
||||
Example usage::
|
||||
|
||||
from flask import Flask
|
||||
from flask.ext.mongoengine import MongoEngine
|
||||
from flask.ext.security import Security, MongoEngineUserDatastore
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = 'secret'
|
||||
app.config['MONGODB_DB'] = 'flask_security_example'
|
||||
app.config['MONGODB_HOST'] = 'localhost'
|
||||
app.config['MONGODB_PORT'] = 27017
|
||||
|
||||
db = MongoEngine(app)
|
||||
|
||||
class Role(db.Document, RoleMixin):
|
||||
name = db.StringField(required=True, unique=True, max_length=80)
|
||||
|
||||
class User(db.Document, UserMixin):
|
||||
email = db.StringField(unique=True, max_length=255)
|
||||
password = db.StringField(required=True, max_length=120)
|
||||
active = db.BooleanField(default=True)
|
||||
roles = db.ListField(db.ReferenceField(Role), default=[])
|
||||
|
||||
Security(app, MongoEngineUserDatastore(db, User, Role))
|
||||
"""
|
||||
|
||||
def _save_model(self, model):
|
||||
|
||||
@@ -35,9 +35,9 @@ def unique_user_email(form, field):
|
||||
|
||||
def valid_user_email(form, field):
|
||||
try:
|
||||
_datastore.find_user(email=field.data)
|
||||
form.user = _datastore.find_user(email=field.data)
|
||||
except UserNotFoundError:
|
||||
raise ValidationError('Invalid email address')
|
||||
raise ValidationError('Specified user does not exist')
|
||||
|
||||
|
||||
class EmailFormMixin():
|
||||
@@ -47,6 +47,7 @@ class EmailFormMixin():
|
||||
|
||||
|
||||
class UserEmailFormMixin():
|
||||
user = None
|
||||
email = TextField("Email Address",
|
||||
validators=[email_required,
|
||||
email_validator,
|
||||
@@ -108,7 +109,7 @@ class PasswordlessLoginForm(Form, UserEmailFormMixin):
|
||||
return dict(email=self.email.data)
|
||||
|
||||
|
||||
class LoginForm(Form, EmailFormMixin, PasswordFormMixin):
|
||||
class LoginForm(Form, UserEmailFormMixin, PasswordFormMixin):
|
||||
"""The default login form"""
|
||||
|
||||
remember = BooleanField("Remember Me")
|
||||
|
||||
+21
-18
@@ -15,10 +15,10 @@ from werkzeug.datastructures import MultiDict
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from flask_security.confirmable import confirm_by_token, \
|
||||
send_confirmation_instructions
|
||||
send_confirmation_instructions, requires_confirmation
|
||||
from flask_security.decorators import login_required
|
||||
from flask_security.exceptions import ConfirmationError, BadCredentialsError, \
|
||||
ResetPasswordError, PasswordlessLoginError
|
||||
from flask_security.exceptions import ConfirmationError, ResetPasswordError, \
|
||||
PasswordlessLoginError
|
||||
from flask_security.forms import LoginForm, RegisterForm, ForgotPasswordForm, \
|
||||
ResetPasswordForm, SendConfirmationForm, PasswordlessLoginForm
|
||||
from flask_security.passwordless import send_login_instructions, login_by_token
|
||||
@@ -27,7 +27,7 @@ from flask_security.recoverable import reset_by_token, \
|
||||
from flask_security.signals import user_registered
|
||||
from flask_security.utils import get_url, get_post_login_redirect, do_flash, \
|
||||
get_message, config_value, login_user, logout_user, \
|
||||
anonymous_user_required, url_for_security as url_for
|
||||
anonymous_user_required, url_for_security as url_for, verify_password
|
||||
|
||||
|
||||
# Convenient references
|
||||
@@ -77,37 +77,40 @@ def _ctx(endpoint):
|
||||
@anonymous_user_required
|
||||
def login():
|
||||
"""View function for login view"""
|
||||
form = LoginForm(request.form, csrf_enabled=not app.testing)
|
||||
|
||||
user, msg, confirm_url = None, None, None
|
||||
form = LoginForm(request.form, csrf_enabled=not app.testing)
|
||||
|
||||
if request.json:
|
||||
form = LoginForm(MultiDict(request.json), csrf_enabled=not app.testing)
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
user = _security.auth_provider.authenticate(form)
|
||||
except ConfirmationError, e:
|
||||
msg = str(e)
|
||||
confirm_url = url_for('send_confirmation', email=e.user.email)
|
||||
except BadCredentialsError, e:
|
||||
msg = str(e)
|
||||
form.password.errors.append(msg)
|
||||
user = form.user
|
||||
|
||||
if user:
|
||||
if requires_confirmation(user):
|
||||
msg = get_message('CONFIRMATION_REQUIRED')
|
||||
confirm_url = url_for('send_confirmation', email=user.email)
|
||||
form.email.errors.append(msg[0])
|
||||
|
||||
elif verify_password(form.password.data, user.password):
|
||||
if login_user(user, remember=form.remember.data):
|
||||
after_this_request(_commit)
|
||||
if request.json:
|
||||
return _json_auth_ok(user)
|
||||
return redirect(get_post_login_redirect())
|
||||
form.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
|
||||
msg = get_message('DISABLED_ACCOUNT')
|
||||
form.email.errors.append(msg[0])
|
||||
else:
|
||||
msg = get_message('PASSWORD_MISMATCH')
|
||||
form.password.errors.append(msg[0])
|
||||
|
||||
_logger.debug('Unsuccessful authentication attempt: %s' % msg)
|
||||
_logger.debug('Unsuccessful authentication attempt: %s' % msg[0])
|
||||
|
||||
if request.json:
|
||||
return _json_auth_error(msg)
|
||||
return _json_auth_error(msg[0])
|
||||
|
||||
if confirm_url:
|
||||
do_flash(msg, 'error')
|
||||
do_flash(*msg)
|
||||
return redirect(confirm_url)
|
||||
|
||||
return render_template('security/login.html',
|
||||
|
||||
Reference in New Issue
Block a user