Edgewall Software
Modify

Opened 2 weeks ago

Last modified 9 days ago

#13792 assigned defect

AttributeError: is_authenticated

Reported by: Dirk Stöcker Owned by: Jun Omae
Priority: normal Milestone: 1.6.1
Component: general Version: 1.6
Severity: normal Keywords:
Cc: Branch:
Release Notes:
API Changes:
Internal Changes:

Description (last modified by Dirk Stöcker)

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 609, in dispatch_request
    dispatcher.dispatch(req)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 301, in dispatch
    raise e
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 210, in dispatch
    chosen_handler = self._get_valid_default_handler(req)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 337, in _get_valid_default_handler
    name = req.session.get('default_handler')
  File "/usr/local/lib/python3.10/dist-packages/trac/web/api.py", line 661, in __getattr__
    value = self.callbacks[name](self)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 364, in _get_session
    return Session(self.env, req)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/session.py", line 266, in __init__
    if not req.is_authenticated:
  File "/usr/local/lib/python3.10/dist-packages/trac/web/api.py", line 664, in __getattr__
    raise AttributeError(name)
AttributeError: is_authenticated

We got multiple reports since Trac 1.6 which report an error which is only temporary and wasn't reproducible for us.

Original reports and duplicates here: https://josm.openstreetmap.de/ticket/23394

Log shows 116 entries since May 2024.

I don't understand the @lazy stuff in python and found no good description, so I can't debug it myself.

Attachments (0)

Change History (8)

comment:1 by Dirk Stöcker, 2 weeks ago

Description: modified (diff)

comment:2 by Jun Omae, 2 weeks ago

Milestone: 1.6.1
Owner: set to Jun Omae
Status: newassigned

Thanks for the reporting. I'll investigate it.

comment:3 by Jun Omae, 2 weeks ago

Unable to reproduce it to me.

Hmm, I'm considering that AttributeError was raised from req.authname in req.is_authenticated. So that, it is probably caused by LoginModule.authenticate (in Trac or th:AccountManagerPlugin).

  • trac/web/api.py

    diff --git a/trac/web/api.py b/trac/web/api.py
    index 7f8b59bdc..fb46051e6 100644
    a b class Request(object):  
    685685    def __getattr__(self, name):
    686686        """Performs lazy attribute lookup by delegating to the functions in the
    687687        callbacks dictionary."""
    688         if name in self.callbacks:
    689             value = self.callbacks[name](self)
    690             setattr(self, name, value)
    691             return value
     688        try:
     689            if name in self.callbacks:
     690                value = self.callbacks[name](self)
     691                setattr(self, name, value)
     692                return value
     693        except AttributeError as e:
     694            raise ValueError('AttributeError caught while resolving %s '
     695                             'attribute' % name)
    692696        raise AttributeError(name)
    693697
    694698    def __repr__(self):

comment:4 by Dirk Stöcker, 2 weeks ago

Why do you believe it is the if? Line 664 is raise AttributeError(name) which also matches the error. So that would be the line after your change.

My interpretation is that Request.__getattr__("is_authenticated") is called instead of Request.is_authenticated(). But as said, I don't fully understand the programm flow yet, but at east I found the @lazy code which it seems mainly caches the call result.

comment:5 by Jun Omae, 2 weeks ago

Demonstration:

>>> from trac.web.api import Request
>>> environ = {'SERVER_PORT': '80', 'SERVER_NAME': 'localhost', 'wsgi.url_scheme': 'http'}
>>> start_response = lambda: ()
>>> req = Request(environ, start_response)
>>> def autenticate(req):
...   raise AttributeError('from autenticate')
...
>>> req.callbacks['authname'] = autenticate
>>> req.authname
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jun66j5/src/tracdev/git/trac/web/api.py", line 689, in __getattr__
    value = self.callbacks[name](self)
  File "<stdin>", line 2, in autenticate
AttributeError: from autenticate
>>> req.is_authenticated
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jun66j5/src/tracdev/git/trac/web/api.py", line 692, in __getattr__
    raise AttributeError(name)
AttributeError: is_authenticated
  1. Read req.is_authenticated attribute
  2. Read req.authname in the is_authenticated method
  3. The callback for authname raises AttributeError
  4. is_authenticated method raises the AttributeError
  5. Python interpreter tries to resolve is_authenticated from __getattr__ because AttributeError is raised from is_authenticated
  6. AttributeError(name) is raised because is_authenticated is not in the callbacks
  7. We received the AttributeError from __getattr__

Applying the patch in comment:3 and the following patch:

  • trac/util/__init__.py

    diff --git a/trac/util/__init__.py b/trac/util/__init__.py
    index f4804e01b..3cb400719 100644
    a b class lazy(object):  
    12981298        functools.update_wrapper(self, fn)
    12991299
    13001300    def __get__(self, instance, owner):
    1301         if instance is None:
    1302             return self
    1303         if self.fn.__name__ in instance.__dict__:
    1304             return instance.__dict__[self.fn.__name__]
    1305         result = self.fn(instance)
    1306         instance.__dict__[self.fn.__name__] = result
    1307         return result
     1301        try:
     1302            if instance is None:
     1303                return self
     1304            if self.fn.__name__ in instance.__dict__:
     1305                return instance.__dict__[self.fn.__name__]
     1306            result = self.fn(instance)
     1307            instance.__dict__[self.fn.__name__] = result
     1308            return result
     1309        except AttributeError:
     1310            raise ValueError('AttributeError caught while resolving %s '
     1311                             'attribute' % self.fn.__name__)
    13081312
    13091313    def __set__(self, instance, value):
    13101314        instance.__dict__[self.fn.__name__] = value

After the patch, trying demonstration code:

>>> req.authname
Traceback (most recent call last):
  File "/home/jun66j5/src/tracdev/git/trac/web/api.py", line 690, in __getattr__
    value = self.callbacks[name](self)
  File "<stdin>", line 2, in autenticate
AttributeError: from autenticate

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jun66j5/src/tracdev/git/trac/web/api.py", line 694, in __getattr__
    raise ValueError('AttributeError caught while resolving %s '
ValueError: AttributeError caught while resolving authname attribute
>>> req.is_authenticated
Traceback (most recent call last):
  File "/home/jun66j5/src/tracdev/git/trac/web/api.py", line 690, in __getattr__
    value = self.callbacks[name](self)
  File "<stdin>", line 2, in autenticate
AttributeError: from autenticate

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jun66j5/src/tracdev/git/trac/util/__init__.py", line 1306, in __get__
    result = self.fn(instance)
  File "/home/jun66j5/src/tracdev/git/trac/web/api.py", line 713, in is_authenticated
    return self.authname and self.authname != 'anonymous'
  File "/home/jun66j5/src/tracdev/git/trac/web/api.py", line 694, in __getattr__
    raise ValueError('AttributeError caught while resolving %s '
ValueError: AttributeError caught while resolving authname attribute
>>>
Last edited 2 weeks ago by Jun Omae (previous) (diff)

comment:6 by Dirk Stöcker, 2 weeks ago

Applied. I'll report results.

comment:7 by Dirk Stöcker, 10 days ago

It seems you're right and this may be caused by auth manager plugin.

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/trac/web/api.py", line 662, in __getattr__
    value = self.callbacks[name](self)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 180, in authenticate
    authname = authenticator.authenticate(req)
  File "/usr/local/lib/python3.10/dist-packages/acct_mgr/util.py", line 54, in wrap
    return func(self, *args, **kwds)
  File "/usr/local/lib/python3.10/dist-packages/acct_mgr/web_ui.py", line 488, in authenticate
    return super(LoginModule, self).authenticate(req)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/auth.py", line 97, in authenticate
    authname = self._get_name_for_cookie(req,
  File "/usr/local/lib/python3.10/dist-packages/acct_mgr/web_ui.py", line 596, in _get_name_for_cookie
    cookie.value = hex_entropy()
AttributeError: can't set attribute 'value'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 609, in dispatch_request
    dispatcher.dispatch(req)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 301, in dispatch
    raise e
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 214, in dispatch
    chosen_handler = self._pre_process_request(req, chosen_handler)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 456, in _pre_process_request
    chosen_handler = filter_.pre_process_request(req, chosen_handler)
  File "/usr/local/lib/python3.10/dist-packages/acct_mgr/api.py", line 469, in pre_process_request
    if not req.session.authenticated or 'ACCTMGR_USER_ADMIN' in req.perm:
  File "/usr/local/lib/python3.10/dist-packages/trac/web/api.py", line 662, in __getattr__
    value = self.callbacks[name](self)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/main.py", line 364, in _get_session
    return Session(self.env, req)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/session.py", line 266, in __init__
    if not req.is_authenticated:
  File "/usr/local/lib/python3.10/dist-packages/trac/util/__init__.py", line 1306, in __get__
    result = self.fn(instance)
  File "/usr/local/lib/python3.10/dist-packages/trac/web/api.py", line 685, in is_authenticated
    return self.authname and self.authname != 'anonymous'
  File "/usr/local/lib/python3.10/dist-packages/trac/web/api.py", line 666, in __getattr__
    raise ValueError('AttributeError caught while resolving %s '
ValueError: AttributeError caught while resolving authname attribute

comment:8 by Jun Omae, 9 days ago

Okay. That is an issue of th:AccountManagerPlugin.

  • acct_mgr/web_ui.py

    diff --git a/acct_mgr/web_ui.py b/acct_mgr/web_ui.py
    index 8f409b82c..3185f4e03 100644
    a b class LoginModule(auth.LoginModule):  
    593593            if random.random() + self.cookie_refresh_pct / 100.0 > 1:
    594594                old_cookie = cookie.value
    595595                # Update auth cookie value
    596                 cookie.value = hex_entropy()
     596                new_cookie = hex_entropy()
     597                cookie.set('value', new_cookie, new_cookie)
    597598                self.log.debug("Changing session id for user %s to %s", name,
    598599                               cookie.value)
    599600
Python 3.11.10 (main, Sep  7 2024, 18:35:42) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from trac.web.api import Cookie
>>> cookies = Cookie('name1=value1; name2=value2')
>>> cookies
<Cookie: name1='value1' name2='value2'>
>>> cookies['name1']
<Morsel: name1=value1>
>>> cookies['name1'].value = '?'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: property 'value' of 'Morsel' object has no setter
>>> cookies['name1'].value = 'vvv'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: property 'value' of 'Morsel' object has no setter
>>> cookies['name1'].set('value', 'vvvv', 'vvvv')
>>> cookies['name1'].value
'vvvv'
>>>

Modify Ticket

Change Properties
Set your email in Preferences
Action
as assigned The owner will remain Jun Omae.
The ticket will be disowned. Next status will be 'new'.
as The resolution will be set. Next status will be 'closed'.
to The owner will be changed from Jun Omae to the specified user.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.