feat(fusion_login_audit): hook unknown-user failures via _login
Overrides res.users._login. When the login string does not resolve to any user, super() raises AccessDenied; we record a row with user_id=NULL and failure_reason="unknown_user", then re-raise. Closes the gap where typo'd or scanned logins would otherwise vanish from the audit trail. The existing _fc_record_login_event helper writes through an independent registry.cursor(), so the audit row survives the rollback that follows the re-raised AccessDenied. Note: in Odoo 19 _login is a plain instance method (not the classmethod it was in earlier versions) and takes (credential, user_agent_env). The original plan was written for the classmethod signature; corrected here and recorded in CLAUDE.md rule #10 so future-Claude does not waste time re-discovering it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -165,3 +165,31 @@ class ResUsers(models.Model):
|
||||
_credential=credential,
|
||||
)
|
||||
raise
|
||||
|
||||
def _login(self, credential, user_agent_env):
|
||||
"""Catch the unknown-user branch of upstream _login.
|
||||
|
||||
In Odoo 19 ``_login`` is an *instance* method (not a classmethod as in
|
||||
earlier versions). Upstream raises ``AccessDenied`` when the login
|
||||
string does not resolve to any user — at that point no user record
|
||||
exists, so the ``bad_password`` path in ``_check_credentials`` never
|
||||
fires. We catch the propagating exception here and write a row with
|
||||
``user_id=NULL`` and ``failure_reason='unknown_user'``.
|
||||
|
||||
``_fc_record_login_event`` already writes through an INDEPENDENT
|
||||
cursor (``self.env.registry.cursor()``), so the audit row survives
|
||||
the outer transaction rollback that follows the re-raised
|
||||
``AccessDenied``. We never block the re-raise: any audit-side
|
||||
exception is caught + logged inside the helper.
|
||||
"""
|
||||
try:
|
||||
return super()._login(credential, user_agent_env)
|
||||
except AccessDenied:
|
||||
self._fc_record_login_event(
|
||||
result='failure',
|
||||
failure_reason='unknown_user',
|
||||
user_id=False,
|
||||
attempted_login=(credential or {}).get('login') or 'unknown',
|
||||
_credential=credential,
|
||||
)
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user