-
-
Notifications
You must be signed in to change notification settings - Fork 512
Fix adding roles when identity is username #686
base: develop
Are you sure you want to change the base?
Fix adding roles when identity is username #686
Conversation
@singingwolfboy thanks for spotting it. Can you please also add a test case? |
47795c5
to
58952b4
Compare
58952b4
to
d57f6ec
Compare
838bff6
to
ebdfa29
Compare
ebdfa29
to
02100c6
Compare
@jirikuncar In order to add a test case, I had to refactor the way the whole test suite works, to account for User models that don't have an "email" field at all. It took awhile, but it seems to be working now. Ideally, the I'm sure this is way more of a change than you expected, but can you take a look and give me your thoughts and feedback? |
@singingwolfboy I am sorry for very late feedback. I am very in favor on making the identity identifier configurable. I would however try a bit lighter approach when defining new forms. I will post the related comments as code review. |
@@ -142,6 +144,8 @@ | |||
_('Invalid confirmation token.'), 'error'), | |||
'EMAIL_ALREADY_ASSOCIATED': ( | |||
_('%(email)s is already associated with an account.'), 'error'), | |||
'USERNAME_ALREADY_IN_USE': ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we go with something more generic like: IDENTIFIER_ALREADY_IN_USE
?
@@ -177,6 +181,10 @@ | |||
_('Email not provided'), 'error'), | |||
'INVALID_EMAIL_ADDRESS': ( | |||
_('Invalid email address'), 'error'), | |||
'USERNAME_NOT_PROVIDED': ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similarly here I would propose IDENTIFIER_NOT_PROVIDED
@@ -177,6 +181,10 @@ | |||
_('Email not provided'), 'error'), | |||
'INVALID_EMAIL_ADDRESS': ( | |||
_('Invalid email address'), 'error'), | |||
'USERNAME_NOT_PROVIDED': ( | |||
_('Username not provided'), 'error'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we decide to be more generic the Username
should be configurable %(identifier_name)s
.
@@ -205,10 +213,21 @@ | |||
_('Please reauthenticate to access this page.'), 'info'), | |||
} | |||
|
|||
_default_forms = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather see this part unchanged and add the logic to the forms itself.
"SECURITY_USER_IDENTITY_ATTRIBUTES", | ||
["email"], | ||
) | ||
if ident_attrs == ["email"]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about more generic condition "email" in identity_attributes
?
@@ -342,7 +361,16 @@ def _get_state(app, datastore, anonymous_user=None, **kwargs): | |||
_unauthorized_callback=None | |||
)) | |||
|
|||
for key, value in _default_forms.items(): | |||
ident_attrs = app.config.get( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't shorten the variable names - I would prefer identity_attributes
for readability purposes.
@@ -118,7 +118,8 @@ def __init__(self, user_model, role_model): | |||
|
|||
def _prepare_role_modify_args(self, user, role): | |||
if isinstance(user, string_types): | |||
user = self.find_user(email=user) | |||
user_kwargs = {attr: user for attr in get_identity_attributes()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 this is very cool! I really like it - great idea 🎉
class IdentifierForm(Form): | ||
def __init__(self, *args, **kwargs): | ||
super(IdentifierForm, self).__init__(*args, **kwargs) | ||
setattr(self, "identifier", getattr(self, self.identifier_field)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO the default could be read from current_app.config[...]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively we could try something like:
class IdentifierField(Field):
def __init__(...):
# autodetect label and validators from current_app.config
class AnyForm(Form):
identifier = IdentifierField()
@@ -1,6 +1,8 @@ | |||
{%- if current_user.is_authenticated -%} | |||
<p>Hello {{ current_user.email }}</p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can register new filter: security_user_attribute
so we can do something like: {{ current_user|security_user_attribute }}
.
@@ -18,10 +18,18 @@ | |||
def authenticate( | |||
client, | |||
email="[email protected]", | |||
username="matt", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about calling it identifier
and deprecating email
?
This resolves a bug that manifests under the following conditions:
app.config["SECURITY_USER_IDENTITY_ATTRIBUTES"] = ["username"]
flask roles add <username> <role>
Traceback:
I'm not sure what's the best way to write a test for this. None of the automated tests seem to handle the case where we have a nonstandard identity attribute.