Repository: ckan/ckan

Issue:
wrong counts in group_list with include_dataset_count=True
## CKAN version
all

## Describe the bug
Permission labels aren't checked for group or organization dataset counts leading to incorrect counts

### Steps to reproduce
Use an IPermissionLabels plugin or package collaborators to give permission to a dataset to a user that
wouldn't normally be able to view it. When that user views the group or organization page that dataset isn't
included in the package counts shown.

### Expected behavior
dataset counts in group_list (and by extension group and organization pages) should match the counts
on the dataset search pages

### Additional details
https://github.com/ckan/ckan/blob/9956f92925740957b119bd7c6719ee202eb51082/ckan/lib/dictization/model_dictize.py#L326-L333 is performing a solr query that mimics the one
in `package_search` but is incomplete. We're better off moving this up to the logic layer and calling
`package_search` so we're sure to get correct results.



Discussion:

Resolving patch:
diff --git a/ckan/lib/dictization/model_dictize.py b/ckan/lib/dictization/model_dictize.py
--- a/ckan/lib/dictization/model_dictize.py
+++ b/ckan/lib/dictization/model_dictize.py
@@ -35,7 +35,6 @@
 import ckan.lib.munge as munge
 import ckan.model as model
 from ckan.types import Context
-from ckan.common import config
 
 ## package save
 
@@ -64,7 +63,8 @@ def group_list_dictize(
     if with_package_counts and 'dataset_counts' not in group_dictize_context:
         # 'dataset_counts' will already be in the context in the case that
         # group_list_dictize recurses via group_dictize (groups in groups)
-        group_dictize_context['dataset_counts'] = get_group_dataset_counts()
+        group_dictize_context['dataset_counts'] = get_group_dataset_counts(
+            logic.fresh_context(context))
     if context.get('with_capacity'):
         group_list = [
             group_dictize(
@@ -323,14 +323,19 @@ def _get_members(context: Context, group: model.Group,
     return q.all()
 
 
-def get_group_dataset_counts() -> dict[str, Any]:
+def get_group_dataset_counts(search_context: Context) -> dict[str, Any]:
     '''For all public groups, return their dataset counts, as a SOLR facet'''
-    query = search.PackageSearchQuery()
-    q: dict[str, Any] = {'q': '',
-         'fl': 'groups', 'facet.field': ['groups', 'owner_org'],
-         'facet.limit': -1, 'rows': 1}
-    query.run(q)
-    return query.facets
+    q: dict[str, Any] = {
+        'facet.limit': -1,
+        'facet.field': ['groups', 'owner_org'],
+        'fl': 'groups',
+        'rows': 0,
+        'include_private': True,
+    }
+    # FIXME: layer violation, like get_packages_for_this_group below
+    search_results = logic.get_action('package_search')(
+        search_context, q)
+    return search_results['facets']
 
 
 def group_dictize(group: model.Group, context: Context,
@@ -376,14 +381,7 @@ def get_packages_for_this_group(group_: model.Group,
                 q['fq'] = '+groups:"{0}"'.format(group_.name)
 
             if group_.is_organization:
-                is_group_member = (context.get('user') and
-                    authz.has_user_permission_for_group_or_org(
-                        group_.id, context.get('user'), 'read'))
-                if is_group_member:
-                    q['include_private'] = True
-                else:
-                    if config.get('ckan.auth.allow_dataset_collaborators'):
-                        q['include_private'] = True
+                q['include_private'] = True
 
             if not just_the_count:
                 # package_search limits 'rows' anyway, so this is only if you
@@ -395,9 +393,7 @@ def get_packages_for_this_group(group_: model.Group,
                 else:
                     q['rows'] = packages_limit
 
-            search_context = cast(
-                Context, dict((k, v) for (k, v) in context.items()
-                              if k != 'schema'))
+            search_context = logic.fresh_context(context)
             search_results = logic.get_action('package_search')(
                 search_context, q)
             return search_results['count'], search_results['results']


Test patch:
diff --git a/ckan/tests/lib/dictization/test_model_dictize.py b/ckan/tests/lib/dictization/test_model_dictize.py
--- a/ckan/tests/lib/dictization/test_model_dictize.py
+++ b/ckan/tests/lib/dictization/test_model_dictize.py
@@ -264,7 +264,7 @@ def test_group_dictize_with_package_count(self):
         context = {
             "model": model,
             "session": model.Session,
-            "dataset_counts": model_dictize.get_group_dataset_counts(),
+            "dataset_counts": model_dictize.get_group_dataset_counts({}),
         }
 
         group = model_dictize.group_dictize(
@@ -308,7 +308,7 @@ def test_group_dictize_for_org_with_package_count(self):
         context = {
             "model": model,
             "session": model.Session,
-            "dataset_counts": model_dictize.get_group_dataset_counts(),
+            "dataset_counts": model_dictize.get_group_dataset_counts({}),
         }
 
         org = model_dictize.group_dictize(