diff --git a/example/app.py b/example/app.py index e739c6d..dfcdbcf 100644 --- a/example/app.py +++ b/example/app.py @@ -123,6 +123,7 @@ def create_sqlalchemy_app(auth_config=None, register_blueprint=True): app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root@localhost/flask_security_test' db = SQLAlchemy(app) + app.db = db roles_users = db.Table('roles_users', db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), @@ -137,7 +138,6 @@ def create_sqlalchemy_app(auth_config=None, register_blueprint=True): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) - remember_token = db.Column(db.String(255)) last_login_at = db.Column(db.DateTime()) current_login_at = db.Column(db.DateTime()) last_login_ip = db.Column(db.String(100)) @@ -173,6 +173,7 @@ def create_mongoengine_app(auth_config=None): app.config['MONGODB_PORT'] = 27017 db = MongoEngine(app) + app.db = db class Role(db.Document, RoleMixin): name = db.StringField(required=True, unique=True, max_length=80) @@ -181,7 +182,6 @@ def create_mongoengine_app(auth_config=None): class User(db.Document, UserMixin): email = db.StringField(unique=True, max_length=255) password = db.StringField(required=True, max_length=255) - remember_token = db.StringField(max_length=255) last_login_at = db.DateTimeField() current_login_at = db.DateTimeField() last_login_ip = db.StringField(max_length=100) diff --git a/flask_security/confirmable.py b/flask_security/confirmable.py index bea5842..839a751 100644 --- a/flask_security/confirmable.py +++ b/flask_security/confirmable.py @@ -73,9 +73,6 @@ def confirm_by_token(token): data = serializer.loads(token, max_age=max_age) user = _datastore.find_user(id=data[0]) - if md5(user.email) != data[1]: - raise UserNotFoundError() - if user.confirmed_at: raise ConfirmationError(get_message('ALREADY_CONFIRMED')) @@ -94,9 +91,6 @@ def confirm_by_token(token): except BadSignature: raise ConfirmationError(get_message('INVALID_CONFIRMATION_TOKEN')) - except UserNotFoundError: - raise ConfirmationError(get_message('INVALID_CONFIRMATION_TOKEN')) - def reset_confirmation_token(user): """Resets the specified user's confirmation token and sends the user diff --git a/flask_security/core.py b/flask_security/core.py index fbef5fc..4c75ba7 100644 --- a/flask_security/core.py +++ b/flask_security/core.py @@ -21,7 +21,7 @@ from werkzeug.local import LocalProxy from . import views, exceptions from .confirmable import requires_confirmation -from .utils import config_value as cv, get_config, verify_password +from .utils import config_value as cv, get_config, verify_password, md5 # Convenient references _security = LocalProxy(lambda: current_app.extensions['security']) @@ -62,6 +62,7 @@ _default_config = { 'CONFIRM_SALT': 'confirm-salt', 'RESET_SALT': 'reset-salt', 'AUTH_SALT': 'auth-salt', + 'REMEMBER_SALT': 'remember-salt', 'DEFAULT_HTTP_AUTH_REALM': 'Login Required' } @@ -81,17 +82,16 @@ _default_flash_messages = { def _user_loader(user_id): try: - return _security.datastore.with_id(user_id) - except Exception, e: - current_app.logger.error('Error getting user: %s' % e) + return _security.datastore.find_user(id=user_id) + except: return None def _token_loader(token): try: - return _security.datastore.find_user(remember_token=token) - except Exception, e: - current_app.logger.error('Error getting user: %s' % e) + data = _security.remember_token_serializer.loads(token) + return _security.datastore.find_user(email=md5(data[0])) + except: return None @@ -137,6 +137,10 @@ def _get_serializer(app, salt): return URLSafeTimedSerializer(secret_key=secret_key, salt=salt) +def _get_remember_token_serializer(app): + return _get_serializer(app, app.config['SECURITY_REMEMBER_SALT']) + + def _get_reset_serializer(app): return _get_serializer(app, app.config['SECURITY_RESET_SALT']) @@ -157,9 +161,6 @@ class RoleMixin(object): def __ne__(self, other): return self.name != other and self.name != getattr(other, 'name', None) - def __str__(self): - return '' % self.name - class UserMixin(BaseUserMixin): """Mixin for `User` model definitions""" @@ -170,7 +171,8 @@ class UserMixin(BaseUserMixin): def get_auth_token(self): """Returns the user's authentication token.""" - self.remember_token + data = [md5(self.email), self.password] + return _security.remember_token_serializer.dumps(data) def has_role(self, role): """Returns `True` if the user identifies with the specified role. @@ -178,10 +180,6 @@ class UserMixin(BaseUserMixin): :param role: A role name or `Role` instance""" return role in self.roles - def __str__(self): - ctx = (str(self.id), self.email) - return '' % ctx - class AnonymousUser(AnonymousUserBase): """AnonymousUser definition""" @@ -262,6 +260,7 @@ class Security(object): ('login_manager', _get_login_manager(app)), ('principal', _get_principal(app)), ('pwd_context', _get_pwd_context(app)), + ('remember_token_serializer', _get_remember_token_serializer(app)), ('token_auth_serializer', _get_token_auth_serializer(app))]: kwargs[key] = value diff --git a/flask_security/datastore.py b/flask_security/datastore.py index e709cb0..1469977 100644 --- a/flask_security/datastore.py +++ b/flask_security/datastore.py @@ -37,6 +37,10 @@ class UserDatastore(object): raise NotImplementedError( "User datastore does not implement _save_model method") + def _delete_model(self, model): + raise NotImplementedError( + "User datastore does not implement _delete_model method") + def _do_with_id(self, id): raise NotImplementedError( "User datastore does not implement _do_with_id method") @@ -118,9 +122,6 @@ class UserDatastore(object): use_hmac=_security.password_hmac) kwargs['password'] = pwd_hash - kwargs['remember_token'] = utils.get_remember_token(kwargs['email'], - kwargs['password']) - return kwargs def with_id(self, id): @@ -170,6 +171,13 @@ class UserDatastore(object): user = self.user_model(**self._prepare_create_user_args(kwargs)) return self._save_model(user) + def delete_user(self, user): + """Delete the specified user + + :param user: The user to delete_user + """ + self._delete_model(user) + def add_role_to_user(self, user, role): """Adds a role to a user if the user does not have it already. Returns the modified user. @@ -248,6 +256,10 @@ class SQLAlchemyUserDatastore(UserDatastore): self.db.session.commit() return model + def _delete_model(self, model): + self.db.session.delete(model) + self.db.session.commit() + def _do_with_id(self, id): return self.user_model.query.get(id) @@ -292,6 +304,9 @@ class MongoEngineUserDatastore(UserDatastore): model.save() return model + def _delete_model(self, model): + model.delete() + def _do_with_id(self, id): try: return self.user_model.objects.get(id=id) diff --git a/flask_security/utils.py b/flask_security/utils.py index ef84006..0706f4f 100644 --- a/flask_security/utils.py +++ b/flask_security/utils.py @@ -44,9 +44,6 @@ def login_user(user, remember=True): 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 @@ -110,12 +107,6 @@ def generate_token(): return base64.urlsafe_b64encode(os.urandom(30)) -def get_remember_token(email, password): - assert email is not None - assert password is not None - return make_secure_token(email, password) - - def do_flash(message, category=None): """Flash a message depending on if the `FLASH_MESSAGES` configuration value is set.