ckan / ckanapi

A command line interface and Python module for accessing the CKAN Action API
Other
176 stars 74 forks source link

reset_db causes NotAuthorized #136

Closed torfsen closed 5 years ago

torfsen commented 5 years ago

I'm using ckanapi.LocalCKAN for the tests of one of our CKAN extensions, and I'm running into problems when I call ckan.tests.helpers.reset_db inbetween test cases: afterwards, some (but not all) action function calls made via ckanapi raise NotAuthorized.

Here is a script that reproduces the issue for me (you can probably ignore the setup of the CKAN environment, it's required to be able to run this in a standalone script). The script expects the path of a CKAN INI as a parameter.

from ckan.tests.helpers import reset_db

import ckanapi

#
# BEGIN SETUP OF CKAN ENV
#

# Adapted from ckanext-archiver
def _load_ckan_environment(ini_path):
    '''
    Load CKAN environment.
    '''
    ini_path = os.path.abspath(ini_path)
    logging.config.fileConfig(ini_path, disable_existing_loggers=False)
    conf = paste.deploy.appconfig('config:' + ini_path)
    load_environment(conf.global_conf, conf.local_conf)
    _register_translator()

# Adapted from ckanext-archiver
def _register_translator():
    '''
    Register a translator in this thread.
    '''
    global registry
    try:
        registry
    except NameError:
        registry = Registry()
    registry.prepare()
    global translator_obj
    try:
        translator_obj
    except NameError:
        translator_obj = MockTranslator()
    registry.register(translator, translator_obj)

_load_ckan_environment(sys.argv[1])

#
# END SETUP OF CKAN ENV
#

def create_pkg_and_res(ckan, name):
    pkg = ckan.action.package_create(name=name)
    res = ckan.action.resource_create(package_id=pkg['id'], url='bar')

ckan = ckanapi.LocalCKAN()

create_pkg_and_res(ckan, 'foo1')  # Works
reset_db()
create_pkg_and_res(ckan, 'foo2')  # Fails with NotAuthorized

The first call to create_pkg_and_res works as expected, but the second one (after the reset_db) raises NotAuthorized when calling resource_create:

Traceback (most recent call last):
  File "./notauthorized.py", line 67, in <module>
    create_pkg_and_res(ckan, 'foo2')  # Fails with NotAuthorized
  File "./notauthorized.py", line 61, in create_pkg_and_res
    res = ckan.action.resource_create(package_id=pkg['id'], url='bar')
  File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/ckanapi/common.py", line 51, in action
    return self._ckan.call_action(name, data_dict=kwargs)
  File "/usr/lib/ckan/default/local/lib/python2.7/site-packages/ckanapi/localckan.py", line 71, in call_action
    return self._get_action(action)(context, data_dict)
  File "/var/transparenz.karlsruhe.de/ckan/ckan/logic/__init__.py", line 457, in wrapped
    result = _action(context, data_dict, **kw)
  File "/var/transparenz.karlsruhe.de/ckan/ckan/logic/action/create.py", line 292, in resource_create
    _check_access('resource_create', context, data_dict)
  File "/var/transparenz.karlsruhe.de/ckan/ckan/logic/__init__.py", line 299, in check_access
    raise NotAuthorized(msg)
ckan.logic.NotAuthorized: <function resource_create at 0x7fb48e0eeaa0> requires an authenticated user

Interestingly, the second call to package_create does not raise an exception.

When the reset_db call is removed, both calls to create_pkg_and_res succeed.

I'm running CKAN 2.7.2 and ckanapi 4.1.

torfsen commented 5 years ago

After some debugging, here is what's happening:

  1. reset_db completely clears the database. This includes all user accounts.
  2. The auth logic for package_create is special, because package_create allows anonymous access under some circumstances. As a result, the auth logic for package_create only checks whether a user is given in the context, but not whether that user actually exists. Hence, when ckanapi passes along the site user in the context, package_create succeeds although that site user doesn't actually exist in the DB anymore.
  3. resource_create always requires an actually existing user, so this call fails because the site user has been removed from the DB by reset_db.
  4. When the script is called again, a ckanapi.LocalCKAN instance is created before any action functions are called. This in turn calls LocalCKAN.get_site_username, which calls CKAN's get_site_user, which (undocumentedly) recreates the site user if it doesn't exist. Hence authorization for the site user works again (until reset_db is called the next time).

As a workaround, I'm now calling LocalCKAN.get_site_username after calling reset_db, and this seems to work fine.

I don't think that this is a problem in CKANAPI, hence I will close this issue.