From 326e07f9050b9c5843ab81f25970b110b9c2f62f Mon Sep 17 00:00:00 2001 From: Andrea Manenti Date: Wed, 16 Oct 2024 17:25:21 +0000 Subject: [PATCH] [FIX] password_security: update password_write_date on copy Sometimes users are created from a template user via copy(). This means a password can be passed in the copy values and never seen by write(), so password_write_date may remain inherited from the template user. This forward-port keeps the original fix: - set password_write_date with copy=False - validate signup password against the correct user when 2FA is enabled - add regression coverage for cloned users Forward-port of OCA/server-auth#713 Original commit: f8c40921508f8231c1721428f4ca30008fe484f6 (cherry picked from commit f8c40921508f8231c1721428f4ca30008fe484f6) --- password_security/controllers/main.py | 8 +++++++- password_security/models/res_users.py | 2 +- password_security/tests/test_signup.py | 24 +++++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/password_security/controllers/main.py b/password_security/controllers/main.py index acab823f33..8ebef72426 100644 --- a/password_security/controllers/main.py +++ b/password_security/controllers/main.py @@ -17,7 +17,13 @@ class PasswordSecurityHome(AuthSignupHome): def do_signup(self, qcontext): password = qcontext.get("password") - user = request.env.user + # If 2FA is activated, request.env.user is not updated to the logged-in user + # at this point. In order to do _check_password on the correct user we + # search by login. + user = ( + request.env.user.search([("login", "=", qcontext.get("login"))]) + or request.env.user + ) user._check_password(password) return super().do_signup(qcontext) diff --git a/password_security/models/res_users.py b/password_security/models/res_users.py index 8ee63b5aca..18dd7dd297 100644 --- a/password_security/models/res_users.py +++ b/password_security/models/res_users.py @@ -18,7 +18,7 @@ class ResUsers(models.Model): _inherit = "res.users" password_write_date = fields.Datetime( - "Last password update", default=fields.Datetime.now, readonly=True + "Last password update", default=fields.Datetime.now, readonly=True, copy=False ) password_history_ids = fields.One2many( string="Password History", diff --git a/password_security/tests/test_signup.py b/password_security/tests/test_signup.py index b600f4cd12..f04f4a09b5 100644 --- a/password_security/tests/test_signup.py +++ b/password_security/tests/test_signup.py @@ -4,6 +4,7 @@ from unittest import mock +from freezegun import freeze_time from requests.exceptions import HTTPError from odoo import http @@ -82,7 +83,8 @@ def test_03_create_user_signup(self): # Stronger password: no error raised vals["password"] = "asdQWE12345_3" - login, pwd = self.env["res.users"].signup(vals) + with freeze_time("2020-01-01"): + login, pwd = self.env["res.users"].signup(vals) # check created user created_user = self.env["res.users"].search([("login", "=", "test_user")]) @@ -160,3 +162,23 @@ def test_06_web_auth_signup_invalid_render(self): self.assertEqual( response.headers["Content-Security-Policy"], "frame-ancestors 'self'" ) + + def test_07_cloned_user_password_write_date(self): + """Users that are cloned should have their password_write_date updated""" + partner = self.env["res.partner"].create({"name": "test partner"}) + vals = { + "name": "Test User", + "login": "test_user", + "email": "test_user@odoo.com", + "password": "Test_user_password123$", + "partner_id": partner.id, + } + with freeze_time("2020-01-01"): + self.env["res.users"].signup(vals) + + original_user = self.env["res.users"].search([("login", "=", "test_user")]) + copied_user = original_user.copy() + + self.assertTrue( + copied_user.password_write_date > original_user.password_write_date + )