Repository: ckan/ckan

Issue:
action package_revise does not properly call its auth function
**CKAN version**
2.9

**Describe the bug**
``package_revise`` via the CKAN API does not work (``INTERNAL SERVER ERROR``) if authorization is checked (as admin it works).

**Steps to reproduce**
Call ``package_revise`` from the API. I have no time to set up a minimal example. But I used python requests library.

**Expected behavior**
It works.

**Additional details**
The UWSGI server log says

```
2020-10-01 20:50:46,735 ERROR [ckan.views.api] 'update'
Traceback (most recent call last):
  File "/usr/lib/ckan/default/src/ckan/ckan/config/middleware/../../views/api.py", line 291, in action
    result = function(context, request_data)
  File "/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py", line 473, in wrapped
    result = _action(context, data_dict, **kw)
  File "/usr/lib/ckan/default/src/ckan/ckan/logic/action/update.py", line 510, in package_revise
    _check_access('package_revise', context, orig)
  File "/usr/lib/ckan/default/src/ckan/ckan/logic/__init__.py", line 309, in check_access
    logic_authorization = authz.is_authorized(action, context,
  File "/usr/lib/ckan/default/src/ckan/ckan/authz.py", line 221, in is_authorized
    return auth_function(context, data_dict)
  File "/usr/lib/ckan/default/src/ckan/ckan/logic/auth/update.py", line 63, in package_revise
    return authz.is_authorized('package_update', context, data_dict['update'])
KeyError: 'update'
```

**Proposed fix**
Changing this line: https://github.com/ckan/ckan/blob/2.9/ckan/logic/action/update.py#L510

```python
_check_access('package_revise', context, orig)
```

to

```python
_check_access('package_revise', context, {"update": orig})
```

resolves the issue for me. The problem is that the auth function expects the "update" key: https://github.com/ckan/ckan/blob/2.9/ckan/logic/auth/update.py#L63

I can create a PR unless you need further proof.


Discussion:

Resolving patch:
diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py
--- a/ckan/logic/action/update.py
+++ b/ckan/logic/action/update.py
@@ -507,7 +507,7 @@ def package_revise(context, data_dict):
         model.Session.rollback()
         raise ValidationError([{k: [de.error]}])
 
-    _check_access('package_revise', context, orig)
+    _check_access('package_revise', context, {"update": orig})
 
     # future-proof return dict by putting package data under
     # "package". We will want to return activity info


Test patch:
diff --git a/ckan/tests/logic/action/test_update.py b/ckan/tests/logic/action/test_update.py
--- a/ckan/tests/logic/action/test_update.py
+++ b/ckan/tests/logic/action/test_update.py
@@ -1893,6 +1893,20 @@ def test_revise_resource_replace_all(self):
         assert response['package']['resources'][0]['name'] == 'new name'
         assert response['package']['resources'][0]['url'] == ''
 
+    def test_revise_normal_user(self):
+        user = factories.User()
+        org = factories.Organization(users=[{'name': user['id'], 'capacity': 'admin'}])
+        # make sure normal users can use package_revise
+        context = {'user': user['name'], 'ignore_auth': False}
+        ds = factories.Dataset(owner_org=org['id'])
+        response = helpers.call_action(
+            'package_revise',
+            match={'id': ds['id']},
+            update={'notes': 'new notes'},
+            context=context,
+        )
+        assert response['package']['notes'] == 'new notes'
+
 
 @pytest.mark.usefixtures("clean_db")
 class TestUserPluginExtras(object):