Repository: ckan/ckan

Issue:
Resource DeleteView uses package_delete auth function
**CKAN version**
2.9.6

**Describe the bug**
Allowing deleting resources but not packages is not possible, because https://github.com/ckan/ckan/blob/9f1b5cfaff8c135b589e2ea0275f1286c2e02711/ckan/views/resource.py#L454 checks `package_delete`.

**Steps to reproduce**
Steps to reproduce the behavior:
- Override `package_delete` and `resource_delete` auth functions with a plugin to create a situation where you can delete a resource in a package but not the package itsef
- Navigate to the resource's edit view
- Observe the delete button existing
- Try to delete the resource
- Observe `403 Forbidden:  Unauthorized to delete package`

**Expected behavior**
`resource_delete` auth function defining if a resource can be deleted

**Additional details**
Should be fixable by just changing the auth function. I'll make a PR.



Discussion:

Resolving patch:
diff --git a/ckan/views/resource.py b/ckan/views/resource.py
--- a/ckan/views/resource.py
+++ b/ckan/views/resource.py
@@ -443,7 +443,7 @@ def get(self,
 
 
 class DeleteView(MethodView):
-    def _prepare(self, id: str):
+    def _prepare(self, resource_id: str):
         context = cast(Context, {
             u'model': model,
             u'session': model.Session,
@@ -451,12 +451,14 @@ def _prepare(self, id: str):
             u'auth_user_obj': current_user
         })
         try:
-            check_access(u'package_delete', context, {u'id': id})
+            check_access(u'resource_delete', context, {u'id': resource_id})
         except NotAuthorized:
             return base.abort(
                 403,
-                _(u'Unauthorized to delete package %s') % u''
+                _(u'Unauthorized to delete resource %s') % u''
             )
+        except NotFound:
+            return base.abort(404, _(u'Resource not found'))
         return context
 
     def post(self, package_type: str, id: str, resource_id: str) -> Response:
@@ -465,7 +467,7 @@ def post(self, package_type: str, id: str, resource_id: str) -> Response:
                 u'{}_resource.edit'.format(package_type),
                 resource_id=resource_id, id=id
             )
-        context = self._prepare(id)
+        context = self._prepare(resource_id)
 
         try:
             get_action(u'resource_delete')(context, {u'id': resource_id})
@@ -487,7 +489,7 @@ def post(self, package_type: str, id: str, resource_id: str) -> Response:
             return base.abort(404, _(u'Resource not found'))
 
     def get(self, package_type: str, id: str, resource_id: str) -> str:
-        context = self._prepare(id)
+        context = self._prepare(resource_id)
         try:
             resource_dict = get_action(u'resource_show')(
                 context, {


Test patch:
diff --git a/ckan/tests/controllers/test_package.py b/ckan/tests/controllers/test_package.py
--- a/ckan/tests/controllers/test_package.py
+++ b/ckan/tests/controllers/test_package.py
@@ -1408,7 +1408,7 @@ def test_logged_in_users_cannot_delete_resources_they_do_not_own(
             extra_environ=env
         )
         assert 403 == response.status_code
-        assert helpers.body_contains(response, "Unauthorized to delete package")
+        assert helpers.body_contains(response, "Unauthorized to delete resource")
 
     def test_sysadmins_can_delete_any_resource(self, app, sysadmin):
         owner_org = factories.Organization()