19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:28 +01:00
parent 20ddc1b4a3
commit c0efcc53f5
1162 changed files with 125577 additions and 105287 deletions

View file

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_auth_signup
from . import test_notify_security_update_totp
from . import test_totp

View file

@ -0,0 +1,48 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.base.tests.common import HttpCaseWithUserDemo, HttpCaseWithUserPortal
from odoo import http
from odoo.tests.common import tagged
@tagged('post_install', '-at_install')
class TestAuthSignupFlowWith2faEnforced(HttpCaseWithUserPortal, HttpCaseWithUserDemo):
def setUp(self):
super().setUp()
self.env['res.config.settings'].create(
{
# Activate free signup
'auth_signup_uninvited': 'b2c',
# Enforce 2FA for all users
'auth_totp_enforce': True,
'auth_totp_policy': 'all_required',
}
).execute()
def test_signup_with_2fa_enforced(self):
"""
Check that registration cleanly succeeds with 2FA enabled and enforced
"""
# ensure the company has an email, otherwise the test fails in no_demo
# because there's no source address
self.env.company.email = "mycompany@example.com"
# Get csrf_token
self.authenticate(None, None)
csrf_token = http.Request.csrf_token(self)
# Values from login form
name = 'toto'
payload = {
'login': 'toto@example.com',
'name': name,
'password': 'mypassword',
'confirm_password': 'mypassword',
'csrf_token': csrf_token,
}
response = self.url_open('/web/signup', data=payload)
new_user = self.env['res.users'].search([('name', '=', name)])
self.assertTrue(new_user)
self.assertEqual(response.status_code, 200, "Signup request should succeed with a 200")

View file

@ -0,0 +1,55 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta
from odoo.addons.mail.tests.common import MailCommon
from odoo.tests import tagged, users
@tagged('-at_install', 'post_install', 'mail_tools', 'res_users')
class TestNotifySecurityUpdateTotp(MailCommon):
@users('employee')
def test_security_update_totp_enabled_disabled(self):
recipients = [self.env.user.email_formatted]
with self.mock_mail_gateway():
self.env.user.write({'totp_secret': 'test'})
self.assertSentEmail(
'"YourTestCompany" <your.company@example.com>',
recipients,
subject='Security Update: 2FA Activated',
)
with self.mock_mail_gateway():
self.env.user.write({'totp_secret': False})
self.assertSentEmail(
'"YourTestCompany" <your.company@example.com>',
recipients,
subject='Security Update: 2FA Deactivated',
)
@users('employee')
def test_security_update_trusted_device_added_removed(self):
""" Make sure we notify the user when TOTP trusted devices are removed on his account. """
recipients = [self.env.user.email_formatted]
trusted_device_age = self.env['auth_totp.device']._get_trusted_device_age()
with self.mock_mail_gateway():
self.env['auth_totp.device'].sudo()._generate(
'trusted_device_chrome',
'Chrome on Windows',
datetime.now() + timedelta(seconds=trusted_device_age)
)
self.assertNotSentEmail(recipients)
# now remove the key using the user's relationship
with self.mock_mail_gateway():
self.env['auth_totp.device'].flush_model()
self.env.user.sudo(False)._revoke_all_devices()
self.assertSentEmail(
'"YourTestCompany" <your.company@example.com>',
recipients,
subject='Security Update: Device Removed',
)

View file

@ -1,17 +1,57 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from odoo.tests import tagged
from odoo.addons.base.tests.common import HttpCaseWithUserDemo
from odoo.addons.auth_totp.tests.test_totp import TestTOTPCommon
from datetime import datetime, timedelta
from xmlrpc.client import Fault
from odoo.tests import get_db_name, tagged, HttpCase
from odoo.tools import mute_logger
from odoo.addons.auth_totp.tests.test_totp import TestTOTPMixin
_logger = logging.getLogger(__name__)
@tagged('post_install', '-at_install')
class TestTOTPInvite(TestTOTPCommon, HttpCaseWithUserDemo):
class TestTOTPMail(TestTOTPMixin, HttpCase):
def test_totp_rpc_api_keys_only(self):
db = get_db_name()
login, password = self.user_test.login, self.user_test.login
uid = self.xmlrpc_common.authenticate(db, password, login, {})
# Without TOTP by mail, xmlrpc using password is expected
[result] = self.xmlrpc_object.execute_kw(db, uid, password, 'res.users', 'read', [uid, ['login']])
self.assertEqual(result['login'], login)
# Enable enforcing TOTP by mail
self.env['res.config.settings'].create({
'auth_totp_enforce': True,
'auth_totp_policy': 'all_required'
}).execute()
# With TOTP by mail, xmlrpc using password is not expected
with (
self.assertRaisesRegex(Fault, r'Access Denied'),
self.assertLogs(logger='odoo.addons.base.models.res_users') as log_catcher,
mute_logger("odoo.http")
):
self.xmlrpc_object.execute_kw(db, uid, password, 'res.users', 'read', [uid, ['login']])
self.assertIn("Invalid API key or password-based authentication", log_catcher.output[0])
# Create an API key for the user
api_key = self.env['res.users.apikeys'].with_user(self.user_test)._generate(
None, 'Foo', datetime.now() + timedelta(days=1)
)
# With TOTP by mail, xmlrpc using an API key is expected
[result] = self.xmlrpc_object.execute_kw(db, uid, api_key, 'res.users', 'read', [uid, ['login']])
self.assertEqual(result['login'], login)
@tagged('post_install', '-at_install')
class TestTOTPInvite(TestTOTPMixin, HttpCase):
def test_totp_administration(self):
# If not enabled (like in demo data), landing on res.config will try
@ -19,5 +59,6 @@ class TestTOTPInvite(TestTOTPCommon, HttpCaseWithUserDemo):
group_order_template = self.env.ref('sale_management.group_sale_order_template', raise_if_not_found=False)
if group_order_template:
self.env.ref('base.group_user').write({"implied_ids": [(4, group_order_template.id)]})
self.start_tour('/web', 'totp_admin_invite', login='admin')
self.start_tour('/web', 'totp_admin_self_invite', login='admin')
self.install_totphook()
self.start_tour('/odoo', 'totp_admin_invite', login='admin')
self.start_tour('/odoo', 'totp_admin_self_invite', login='admin')