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

24 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-06 22:25 +0000

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

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING 

6 

7from django.contrib.contenttypes.models import ContentType 

8 

9if TYPE_CHECKING: 

10 from django.contrib.admin import ModelAdmin 

11 from django.db.models import Model 

12 from django.http import HttpRequest 

13 

14 

15def 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 

46def 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