You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TLDR: context['auth_user_obj'] is not always defined, which can lead to unwanted exceptions when calling ckan.authz.py.auth_is_loggedin_user(), especially when implementing IAuthFunctions in a plugin. Years ago, before Flask, there was a PR with a solution for this that was never merged. Now with Flask, this solution doesn't work under all circumstances anymore.
This is a follow-up to the open issues #4183 and #4097. The main point of both was that ckan.authz.py.auth_is_loggedin_user() would fail if auth_user_obj was not defined in the context. This was still in the Pylons-era, before Flask.
The proposed solution was to check ckan.authz.py.auth_is_loggedin_user() instead of looking for the auth_user_obj. The implementation would have looked like this:
This was actually implemented in PR #4222 by @amercader, but was then somehow never merged.
I would like to propose a new PR with this change. However, now I'm running into the problem that this code fails when run from the context of a test, rather than within a running application. This is because apparently there is no Flask application context. The test looks like this:
def test_technical_group_excluded_for_regular(self, app):
> factories.Organization(name='regular')
...
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:46: in __call__
return cls.create(**kwargs)
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:564: in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:501: in _generate
return step.build()
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/builder.py:276: in build
instance = self.factory_meta.instantiate(
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:315: in instantiate
return self.factory._create(model, *args, **kwargs)
/usr/lib/ckan/default/src/ckan/ckan/tests/factories.py:311: in _create
group_dict = helpers.call_action(
/usr/lib/ckan/default/src/ckan/ckan/tests/helpers.py:129: in call_action
return logic.get_action(action_name)(context=context, data_dict=kwargs)
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:504: in wrapped
result = _action(context, data_dict, **kw)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/create.py:931: in organization_create
return _group_or_org_create(context, data_dict, is_org=True)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/create.py:799: in _group_or_org_create
else _get_action(action)(context, {'id': group.id})
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:504: in wrapped
result = _action(context, data_dict, **kw)
ckanext/berlinauth/action/get.py:76: in organization_show
group_dict = ckanget.organization_show(context, data_dict)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/get.py:1302: in organization_show
return _group_or_org_show(context, data_dict, is_org=True)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/get.py:1219: in _group_or_org_show
group_dict['num_followers'] = logic.get_action('group_follower_count')(
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:504: in wrapped
result = _action(context, data_dict, **kw)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/get.py:2754: in group_follower_count
_check_access('group_follower_count', context, data_dict)
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:309: in check_access
logic_authorization = authz.is_authorized(action, context,
/usr/lib/ckan/default/src/ckan/ckan/authz.py:217: in is_authorized
logged_in = context.get('auth_user_obj') or auth_is_loggedin_user()
/usr/lib/ckan/default/src/ckan/ckan/authz.py:537: in auth_is_loggedin_user
context_user = g.user
/usr/lib/ckan/default/lib/python3.8/site-packages/werkzeug/local.py:347: in __getattr__
return getattr(self._get_current_object(), name)
/usr/lib/ckan/default/lib/python3.8/site-packages/werkzeug/local.py:347: in __getattr__
return getattr(self._get_current_object(), name)
/usr/lib/ckan/default/lib/python3.8/site-packages/werkzeug/local.py:306: in _get_current_object
return self.__local()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
name = 'g'
def _lookup_app_object(name):
top = _app_ctx_stack.top
if top is None:
> raise RuntimeError(_app_ctx_err_msg)
E RuntimeError: Working outside of application context.
E
E This typically means that you attempted to use functionality that needed
E to interface with the current application object in some way. To solve
E this, set up an application context with app.app_context(). See the
E documentation for more information.
/usr/lib/ckan/default/lib/python3.8/site-packages/flask/globals.py:45: RuntimeError
I have tried to follow the suggestion in the error message and wrapped the relevant calls in the test in a with app.flask_app.app_context():, but now I get an AttributeError: '_Globals' object has no attribute 'user':
def test_technical_group_excluded_for_regular(self, app):
with app.flask_app.app_context():
> factories.Organization(name='regular')
ckanext/berlinauth/tests/test_action_get.py:51:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:46: in __call__
return cls.create(**kwargs)
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:564: in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:501: in _generate
return step.build()
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/builder.py:276: in build
instance = self.factory_meta.instantiate(
/usr/lib/ckan/default/lib/python3.8/site-packages/factory/base.py:315: in instantiate
return self.factory._create(model, *args, **kwargs)
/usr/lib/ckan/default/src/ckan/ckan/tests/factories.py:311: in _create
group_dict = helpers.call_action(
/usr/lib/ckan/default/src/ckan/ckan/tests/helpers.py:129: in call_action
return logic.get_action(action_name)(context=context, data_dict=kwargs)
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:504: in wrapped
result = _action(context, data_dict, **kw)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/create.py:931: in organization_create
return _group_or_org_create(context, data_dict, is_org=True)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/create.py:799: in _group_or_org_create
else _get_action(action)(context, {'id': group.id})
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:504: in wrapped
result = _action(context, data_dict, **kw)
ckanext/berlinauth/action/get.py:76: in organization_show
group_dict = ckanget.organization_show(context, data_dict)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/get.py:1302: in organization_show
return _group_or_org_show(context, data_dict, is_org=True)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/get.py:1219: in _group_or_org_show
group_dict['num_followers'] = logic.get_action('group_follower_count')(
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:504: in wrapped
result = _action(context, data_dict, **kw)
/usr/lib/ckan/default/src/ckan/ckan/logic/action/get.py:2757: in group_follower_count
_check_access('group_follower_count', context, data_dict)
/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py:309: in check_access
logic_authorization = authz.is_authorized(action, context,
/usr/lib/ckan/default/src/ckan/ckan/authz.py:220: in is_authorized
logged_in = context.get('auth_user_obj') or auth_is_loggedin_user()
/usr/lib/ckan/default/src/ckan/ckan/authz.py:542: in auth_is_loggedin_user
context_user = g.user
/usr/lib/ckan/default/lib/python3.8/site-packages/werkzeug/local.py:347: in __getattr__
return getattr(self._get_current_object(), name)
/usr/lib/ckan/default/lib/python3.8/site-packages/werkzeug/local.py:347: in __getattr__
return getattr(self._get_current_object(), name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <ckan.config.middleware.flask_app.CKAN_AppCtxGlobals object at 0x7fc10c063790>, name = 'user'
def __getattr__(self, name):
'''
If flask.g doesn't have attribute `name`, fall back to CKAN's
app_globals object.
If the key is also not found in there, an AttributeError will be raised
'''
> return getattr(app_globals.app_globals, name)
E AttributeError: '_Globals' object has no attribute 'user'
I'm a bit conflicted about this whole issue report, because I'm not sure if the main problem here is the missing auth_user_obj, or the missing/incomplete application context, or both.
If necessary, I can try to provide a minimal plugin that illustrates this problem, but I'm hoping the issue can be discussed without this.
Steps to reproduce
Steps to reproduce the behavior:
Expected behavior
A clear and concise description of what you expected to happen.
Additional details
If possible, please provide the full stack trace of the error raised, or add screenshots to help explain your problem.
The text was updated successfully, but these errors were encountered:
CKAN version
2.9.5
Describe the bug
TLDR:
context['auth_user_obj']
is not always defined, which can lead to unwanted exceptions when callingckan.authz.py.auth_is_loggedin_user()
, especially when implementing IAuthFunctions in a plugin. Years ago, before Flask, there was a PR with a solution for this that was never merged. Now with Flask, this solution doesn't work under all circumstances anymore.This is a follow-up to the open issues #4183 and #4097. The main point of both was that
ckan.authz.py.auth_is_loggedin_user()
would fail ifauth_user_obj
was not defined in thecontext
. This was still in the Pylons-era, before Flask.ckan/ckan/authz.py
Lines 173 to 174 in bc4f8df
The proposed solution was to check
ckan.authz.py.auth_is_loggedin_user()
instead of looking for theauth_user_obj
. The implementation would have looked like this:This was actually implemented in PR #4222 by @amercader, but was then somehow never merged.
I would like to propose a new PR with this change. However, now I'm running into the problem that this code fails when run from the context of a test, rather than within a running application. This is because apparently there is no Flask application context. The test looks like this:
I have tried to follow the suggestion in the error message and wrapped the relevant calls in the test in a
with app.flask_app.app_context():
, but now I get anAttributeError: '_Globals' object has no attribute 'user'
:I'm a bit conflicted about this whole issue report, because I'm not sure if the main problem here is the missing
auth_user_obj
, or the missing/incomplete application context, or both.If necessary, I can try to provide a minimal plugin that illustrates this problem, but I'm hoping the issue can be discussed without this.
Steps to reproduce
Steps to reproduce the behavior:
Expected behavior
A clear and concise description of what you expected to happen.
Additional details
If possible, please provide the full stack trace of the error raised, or add screenshots to help explain your problem.
The text was updated successfully, but these errors were encountered: