feat(fusion_login_audit): hook successful login via _update_last_login
Overrides res.users._update_last_login to create a fusion.login.audit row with result=success after the parent runs. The write goes through sudo() + mail_create_nolog=True. Any exception in the audit path is caught and logged but never propagates — a broken audit table must never block a real user from logging in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,3 +86,39 @@ class TestFusionLoginAuditModel(TransactionCase):
|
||||
serialized = repr(vals)
|
||||
self.assertNotIn('super-secret-pw', serialized)
|
||||
self.assertEqual(vals['failure_reason'], 'bad_password')
|
||||
|
||||
def test_update_last_login_writes_audit_row(self):
|
||||
"""Calling _update_last_login on a user creates a success row."""
|
||||
user = self.env['res.users'].sudo().create({
|
||||
'name': 'Audit Tester',
|
||||
'login': 'audit-tester@example.com',
|
||||
'password': 'audit-tester-pw-1',
|
||||
})
|
||||
Audit = self.env['fusion.login.audit'].sudo()
|
||||
before = Audit.search_count([('user_id', '=', user.id)])
|
||||
user._update_last_login()
|
||||
after = Audit.search_count([('user_id', '=', user.id)])
|
||||
self.assertEqual(after, before + 1)
|
||||
row = Audit.search([('user_id', '=', user.id)],
|
||||
order='event_time desc', limit=1)
|
||||
self.assertEqual(row.result, 'success')
|
||||
self.assertEqual(row.attempted_login, user.login)
|
||||
self.assertFalse(row.failure_reason)
|
||||
self.assertEqual(row.database, self.env.cr.dbname)
|
||||
|
||||
def test_audit_write_failure_does_not_block_login(self):
|
||||
"""An exception inside the audit write must not propagate."""
|
||||
from unittest.mock import patch
|
||||
user = self.env['res.users'].sudo().create({
|
||||
'name': 'Resilient Tester',
|
||||
'login': 'resilient-tester@example.com',
|
||||
'password': 'resilient-tester-pw-1',
|
||||
})
|
||||
|
||||
def boom(self_, vals):
|
||||
raise RuntimeError('simulated audit DB failure')
|
||||
|
||||
with patch.object(type(self.env['fusion.login.audit']),
|
||||
'create', boom):
|
||||
# Must not raise.
|
||||
user._update_last_login()
|
||||
|
||||
Reference in New Issue
Block a user