0

I updated my user model from:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(255))
    last_name = db.Column(db.String(255))
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean())
    confirmation = db.Column(db.Boolean)
    email_confirmation_sent_on = db.Column(db.DateTime, nullable=True)
    confirmed_at = db.Column(db.DateTime, nullable=True)
    registered_on = db.Column(db.DateTime, nullable=True)
    last_login_at = db.Column(db.DateTime, nullable=True)
    current_login_at = db.Column(db.DateTime, nullable=True)
    last_login_ip = db.Column(db.String)
    current_login_ip = db.Column(db.String)
    login_count = db.Column(db.Integer)
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))

To:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(255))
    last_name = db.Column(db.String(255))
    email = db.Column(db.String(255), unique=True)
    _password = db.Column(db.String(255))
    active = db.Column(db.Boolean())
    confirmation = db.Column(db.Boolean)
    email_confirmation_sent_on = db.Column(db.DateTime, nullable=True)
    confirmed_at = db.Column(db.DateTime, nullable=True)
    registered_on = db.Column(db.DateTime, nullable=True)
    last_login_at = db.Column(db.DateTime, nullable=True)
    current_login_at = db.Column(db.DateTime, nullable=True)
    last_login_ip = db.Column(db.String)
    current_login_ip = db.Column(db.String)
    login_count = db.Column(db.Integer)
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))


    @hybrid_property
    def password(self):
        return self._password

    @password.setter
    def set_password(self, plaintext):
        self._password = hash_password(plaintext)

So, that I could hash passwords from the User model. However, now I can't login to the user backend. I think it is because it doesn't recognize _password on the user built-in forms? Is there a way I can take advantage of the @password.setter decorator and still use Flask-security?

spitfiredd
  • 2,897
  • 5
  • 32
  • 75

1 Answers1

0

I'm not super familiar with Flask-Security, so this is a bit of a guess:

Flask-Security is probably using your password property, but your own password hashing and Flask-Security's are probably interfering with each other.

You could test this by just redefining set_password to simply set the plaintext:

@password.setter
def set_password(self, plaintext):
    self._password = plaintext

If that fixes your login issue, you might be able to get what you want from both flask-admin and flask-security by creating a second property/decorator (both operating on ._password, and point flask-admin at a different one. The hitch is that you'll need to make sure your password hashing algorithms match (so you may want to re-use Flask-Security's in both locations). This might look like:

# models.py
class User(db.Model, UserMixin):
    ...
    _password = db.Column(db.String(255))
    ...

    @hybrid_property
    def password(self):
        return self._password

    @password.setter
    def set_password(self, hashed):
        self._password = hashed

    @hybrid_property
    def override_password(self):
        return self._password

    @override_password.setter
    def override_password(self, plaintext):
        self._password = hash_password(plaintext)

# admin.py
class UserView(MyModelView):
    form_columns = ('email', 'override_password', ...)

It may also be possible to get what you want by customizing/configuring Flask-Security or the underlying packages that handle login flow and password hashing.

abathur
  • 1,047
  • 7
  • 19
  • I posted this question to reddit as well. I got back this response, but I am unsure how to implement it. https://www.reddit.com/r/flask/comments/85owwe/flask_security_how_do_you_get_it_to_recogonize/dvz5teg/ – spitfiredd Mar 20 '18 at 22:04
  • @spitfiredd It looks like that poster responded with a good answer on implementation; we're both taking different paths to the same rough goal. – abathur Mar 21 '18 at 13:52