flask-security icon indicating copy to clipboard operation
flask-security copied to clipboard

Use user.get_id() instead of user.id

Open jimbojetlag opened this issue 9 years ago • 16 comments

https://github.com/mattupstate/flask-security/blob/a0e203774795f1bad289d76357237fcb604bfa01/flask_security/utils.py#L82

Isn't the whole point of Flask-Login interface for get_id() to avoid calling an id attribute directly? I'm implementing a backend for Google datastore which does not have id attibute.

jimbojetlag avatar Jan 25 '16 18:01 jimbojetlag

I'm just playing around with Flask-Security as well, and have run into this issue. I have a legacy DB with no "id" field. So Flask-Security won't work there :(

exhuma avatar Feb 21 '16 16:02 exhuma

Flask-Security uses id in several places, and the docs state that it is a required field of User objects. To get around the issue of not having an id on your user model, you can simply add id as a property and return whatever your actual identifier is.

class User(UserDatastore):
    """Custom Flask-Security-compatible user model."""
    ...
    @property
    def id(self):
        """Return an identifier."""
        return self.unique_identifier

I can't speak to the author's intention with Flask-Login's get_id, but it seems that it exists as much to convert other types to unicode as it does to abstract away an id field; it's effectively just a wrapper unicode(user.id).

jonafato avatar Feb 21 '16 22:02 jonafato

@jonafato After having a good night's sleep, I had the same idea. Wasn't sure if flask-security expected an int though. Just blindly tested and it works.

@jimbojetlag Can you test if that works as well for you and let us know? If yes, this issue could be considered closed if that's okay with you?

Cheers.

exhuma avatar Feb 23 '16 09:02 exhuma

I ended up with the same solution myself. I'm closing this issue, but I do not find the interface consistent.

jimbojetlag avatar Feb 23 '16 16:02 jimbojetlag

It seems this does not work after all. I've been hunting a login-bug in my application for the past two days now, and traced it back to my classes not having the default id attribute.

I've created a gist with a working and broken example. The only difference is the id attribute on both the role and user tables.

@jimbojetlag Did you only add the "property"? Or did you do anything else to make it work? If it turns out that it's because of the id field, then I guess this issue needs to be reopened.

exhuma avatar Feb 26 '16 07:02 exhuma

I forgot to mention that I also had to override a module function in a dirty way:

from flask.ext.principal import UserNeed

def _on_identity_loaded(sender, identity):
    if hasattr(current_user, 'id'):
        identity.provides.add(UserNeed(current_user.id))
    identity.user = current_user
flask_security_core._on_identity_loaded = _on_identity_loaded

jimbojetlag avatar Feb 26 '16 07:02 jimbojetlag

Hmmm... this did not make any difference for me. From what I can tell, comparing to the original _on_identity_loaded method, you only removed the roles. I've stepped through the original method in core.py, and it looks like it does the right thing. The Identity object is properly created (with the correct values).

I only realised that auth_type is None. But looking through flask-security, the Identity constructor is actually used with the default constructor which also sets auth_type to None. So that does not look to be the source of the problem... I think.

I still don't quite see where the problem comes from :(

exhuma avatar Feb 26 '16 17:02 exhuma

Hello, I found this issue after hours trying to find out, why I am not able to confirm my account with a generated token.

The only problem is missing id field in SQLAlchemy DB, because the get_token_status function uses datastore.find_user instead of datastore.get_user, which is simply SQL filter on id field.

Do you have some workaround? I do not like using generic id field in the database... This could be at least configurable, as other model related options.

MartinMikita avatar Jun 21 '17 22:06 MartinMikita

@MartinMikita what is your primary key in User table?

jirikuncar avatar Jun 22 '17 11:06 jirikuncar

Field account_id, why?

MartinMikita avatar Jun 22 '17 11:06 MartinMikita

Can you create an alias property id for it, since Flask-Security expects this property to exists?

jirikuncar avatar Jun 23 '17 06:06 jirikuncar

@jirikuncar Could you, please, read my post again and carefully? It is NOT expected property, but it is using SQL .filter_by(id=value), this will not use alias property in the model! I have it, and it is not working, therefore I wrote it here.

This is the only code in the method get_token_status(), where it is used in this way...

MartinMikita avatar Jun 23 '17 06:06 MartinMikita

@MartinMikita it would be easier if you provide the whole definition of your model.

Following example should work for you:

from sqlalchemy.orm import synonym

class User(db.Model, UserMixin):
    account_id = Column(...)
    ...

    id = synonym('account_id')

PS: I am trying to help you in my free time so be patient 😉

jirikuncar avatar Jun 23 '17 07:06 jirikuncar

Sorry, I haven't heard of synonym from SQLAlchemy before. I was using python property as an alias.

MartinMikita avatar Jun 23 '17 07:06 MartinMikita

@jirikuncar they synonym actually worked for me. Thank you!

d13g0 avatar Jul 19 '17 22:07 d13g0

id = synonym('account_id')

Thank you for the synonym solution

sagard21 avatar Jan 16 '22 07:01 sagard21