Coverage for src/django_global_search/permissions.py: 100%

1"""Permission utilities for global search."""

2

3 from __future__ import annotations

4

5 from typing import TYPE_CHECKING

6

7 from django.contrib.contenttypes.models import ContentType

8

9 if TYPE_CHECKING:

10 from django.contrib.admin import ModelAdmin

11 from django.db.models import Model

12 from django.http import HttpRequest

13

14

15 def has_search_permission(

16 request: HttpRequest,

17 model_admin: ModelAdmin,

18 model: Model,

19 excluded_models: set[str],

20) -> bool:

21 """Check if user has permission to search this model.

22

23 :param request: HTTP request object

24 :param model_admin: ModelAdmin instance

25 :param model: Django model class

26 :param excluded_models: Set of excluded model labels (format: "app_label.model_name")

27 :return: True if user can search this model, False otherwise

28 """

29 # Check if model has search_fields configured

30 if not getattr(model_admin, "search_fields", None):

31 return False

32

33 # Check module permission (app-level access)

34 if not model_admin.has_module_permission(request):

35 return False

36

37 # Check view permission (model-level access)

38 if not model_admin.has_view_permission(request):

39 return False

40

41 # Check if model is in excluded list

42 model_label = f"{model._meta.app_label}.{model._meta.model_name}"

43 return model_label not in excluded_models

44

45

46 def filter_searchable_models(

47 request: HttpRequest,

48 admin_registry: dict,

49 excluded_models: set[str],

50 content_type_ids: list[int] | None = None,

51) -> list[ModelAdmin]:

52 """Filter admin registry to get searchable ModelAdmin instances.

53

54 :param request: HTTP request object

55 :param admin_registry: AdminSite._registry dictionary

56 :param excluded_models: Set of excluded model labels

57 :param content_type_ids: Optional list of content type IDs to filter

58 :return: List of searchable ModelAdmin instances

59 """

60 searchable_admins = []

61 selected_content_type_ids = set(content_type_ids) if content_type_ids else None

62

63 for model, model_admin in admin_registry.items():

64 # Check basic search permission

65 if not has_search_permission(request, model_admin, model, excluded_models):

66 continue

67

68 # Filter by content_type_ids if provided

69 if selected_content_type_ids:

70 content_type = ContentType.objects.get_for_model(model)

71 if content_type.id not in selected_content_type_ids:

72 continue

73

74 searchable_admins.append(model_admin)

75

76 return searchable_admins