-
Notifications
You must be signed in to change notification settings - Fork 30.1k
[IMP] base: unify portal accounts #192098
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
[IMP] base: unify portal accounts #192098
Conversation
8ef1b33 to
f2db526
Compare
69f55f0 to
080ba88
Compare
7588ba9 to
94a6567
Compare
|
Task has been cancelled. |
a5fd08e to
cceeaaf
Compare
78d1376 to
818fec4
Compare
11f3fe8 to
cc71c79
Compare
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.
Hello @kmdi-odoo ,
Left some comments
Thanks
| if qcontext.get('login'): | ||
| qcontext['login'] = qcontext['login'].strip() |
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.
Can't we strip it when preparing qcontext ?
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 am not sure to allow having the same login, once active and once not active is ok regarding the security. At first sight I would say it's ok, but it's been 15+ years that we do not allow this, that the unique constraint is based on the login only and not the active boolean, so there might be a need to think this through correctly
odoo/addons/base/models/res_users.py
Outdated
| (tuple(self.ids),) | ||
| ) | ||
| if self.env.cr.rowcount: | ||
| raise ValidationError(_('User with similar login already exists!')) |
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.
Instead of doing a python constraint, shouldn't you modify the existing SQL constraint ?
_sql_constraints = [
('login_key', 'UNIQUE (login)', 'You can not have two users with the same login!')
]Instead of a UNIQUE (login) constraint, it should be a unique constraint on the lower login + active
The guideline tells that It's always preferred to do constraint in SQL rather than in python if this is possible. Python constraints are used only when it's not possible to do the constraint in SQL. An example is when you need to have a constraint on multiple tables, but this is not the case here.
Regarding the constraint for website_id, where you use a UNION, shouldn't you use a COALESCE instead ?
GROUP BY lower(login), coalesce(website_id, false)That would simplify the query rather than doubling it with a union
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.
Instead of doing a python constraint, shouldn't you modify the existing SQL constraint ?
Yes, I actually thought of modifying existing SQL constraint, but didn't do it after thinking of both approaches:
-
If we are keeping no duplicate case-insensitive login (including archived):
The existing dbs having duplicate case-insensitive logins will break the SQL constraint as the upgrade script will only archive the duplicate users therefore can be done using python constraint. -
Allow only one login for active (case-insensitively unique for unarchived only) (SQL constraint):
- I was really unsure to go with this before asking seniors.
- If we want to do it, we might need to use
models.UniqueIndexasmodels.Constraintdoes not allow conditions and functions to be applied on the constraint. - Seems that
models.UniqueIndexdoesn't show nice error message as we show formodels.Constraint(though we can see what we can do). I was also unsure about performance as creating and deleting the user will make postgres index recomputation.
Regarding the constraint for website_id, where you use a UNION, shouldn't you use a COALESCE instead ?
-> Seems that SQL treats NULLs in the GROUP BY as same groupable value. Anyways, we can always make code robust by using COALESCE:)
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 want to do it, we might need to use models.UniqueIndex as models.Constraint does not allow conditions and functions to be applied on the constraint.
That's correct but not an issue: a unique constraint and a unique index are essentially the same thing, the only difference is some of the more advanced capabilities (e.g. indexes can be partial and created concurrently, but constraints can be deferred), under hood a unique constraints creates and uses a unique index:
Adding a unique constraint will automatically create a unique B-tree index on the column or group of columns listed in the constraint.
which is why you can ADD CONSTRAINT USING INDEX (for a unique or primary constraint).
Seems that models.UniqueIndex doesn't show nice error message as we show for models.Constraint (though we can see what we can do)
It takes a message as second parameter so I'd assume it should:
You can also specify a message to be used when constraint is violated.
that might be something to check with the orm team if it doesn't work correctly.
I was also unsure about performance as creating and deleting the user will make postgres index recomputation.
See above. A unique constraint and a unique index are essentially the same thing, so require the same amount of upkeep.
|
Hello @odoo/rd-security, As @beledouxdenis is off this week, can anyone give this one a look? Thanks |
odoo/addons/base/models/res_users.py
Outdated
| @api.model | ||
| def _get_login_domain(self, login): | ||
| return [('login', '=', login)] | ||
| return [('login', '=ilike', escape_psql(login))] |
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.
That's going to kill the performances of the searches, on DBs with a large number of records it's going to be really bad.
Also this doesn't make sense, since in website the change to the constraint keeps login as case-sensitive if there is no website_id? But this change means all uses of _get_login_domain are now CI.
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.
That's going to kill the performances of the searches, on DBs with a large number of records it's going to be really bad.
Then we should maybe try to query the db directly to search for similar logins?
Also this doesn't make sense, since in
websitethe change to the constraint keepsloginas case-sensitive if there is nowebsite_id? But this change means all uses of_get_login_domainare now CI.
If I am not overlooking, in website the first part of the union was kept as it was before, to avoid having duplicates (CS) with website_id as null. (As UNIQUE constraint treats nulls as not equal).
I think it creates all sort of odd inconsistencies e.g. the notion of portal becomes stranger as the (invisible) Because deactivating an account now moves them out of UNIQUE, if you disable a portal user's account they can now recreate the same account immediately, which I'm not sure is great? |
|
Odo feedback:
|
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.
Hello @xmo-odoo,
Thanks for your comments.
I've answered the questions.
Also, what should be the final verdict for the unicity of the users (for only active or all)?
odoo/addons/base/models/res_users.py
Outdated
| @api.model | ||
| def _get_login_domain(self, login): | ||
| return [('login', '=', login)] | ||
| return [('login', '=ilike', escape_psql(login))] |
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.
That's going to kill the performances of the searches, on DBs with a large number of records it's going to be really bad.
Then we should maybe try to query the db directly to search for similar logins?
Also this doesn't make sense, since in
websitethe change to the constraint keepsloginas case-sensitive if there is nowebsite_id? But this change means all uses of_get_login_domainare now CI.
If I am not overlooking, in website the first part of the union was kept as it was before, to avoid having duplicates (CS) with website_id as null. (As UNIQUE constraint treats nulls as not equal).
d569ea3 to
2d55b23
Compare
This commit adds a validation on the portal user signup. From this commit, new login emails are matched to exisiting logins case-insensitively and by removing the trailing whitespaces. Task-4247745
2d55b23 to
e11a444
Compare
|
Hello @xmo-odoo 👋, I've now updated code to keep unicity across all the users using That will create a unique index on
Can you please have a look if its feasible for large dbs also? Thanks |

This commit adds a validation on the portal user signup. From this commit, new login emails are matched to exisiting logins case-insensitively and by removing the trailing whitespaces.
Task-4247745