|
| 1 | +""" |
| 2 | +Extended Casbin Adapter with Filtering Support. |
| 3 | +
|
| 4 | +This module provides an enhanced adapter implementation for Casbin that extends |
| 5 | +the base Django adapter with filtering capabilities. The ExtendedAdapter allows |
| 6 | +for efficient loading of policy rules from the database with support for |
| 7 | +filtering based on policy attributes. |
| 8 | +
|
| 9 | +The adapter combines functionality from both the base Adapter (for Django ORM |
| 10 | +integration) and FilteredAdapter (for selective policy loading) to provide |
| 11 | +optimized policy management for authorization systems. |
| 12 | +""" |
| 13 | + |
| 14 | +from enum import Enum |
| 15 | + |
| 16 | +from casbin import persist |
| 17 | +from casbin.model import Model |
| 18 | +from casbin.persist import FilteredAdapter |
| 19 | +from casbin_adapter.adapter import Adapter |
| 20 | +from casbin_adapter.models import CasbinRule |
| 21 | +from django.db.models import QuerySet |
| 22 | + |
| 23 | +from openedx_authz.engine.filter import Filter |
| 24 | + |
| 25 | + |
| 26 | +class PolicyAttribute(Enum): |
| 27 | + """ |
| 28 | + Enumeration of Casbin policy attributes. |
| 29 | +
|
| 30 | + These attributes map to the columns of the CasbinRule table, but their meaning |
| 31 | + depends on the policy type (ptype). Check the ``openedx_authz.engine.Filter`` class |
| 32 | + for more details. |
| 33 | + """ |
| 34 | + |
| 35 | + PTYPE = "ptype" |
| 36 | + """ptype (str): Type of policy""" |
| 37 | + |
| 38 | + V0 = "v0" |
| 39 | + """v0 (str): First policy value.""" |
| 40 | + |
| 41 | + V1 = "v1" |
| 42 | + """v1 (str): Second policy value.""" |
| 43 | + |
| 44 | + V2 = "v2" |
| 45 | + """v2 (str): Third policy value.""" |
| 46 | + |
| 47 | + V3 = "v3" |
| 48 | + """v3 (str): Fourth policy value.""" |
| 49 | + |
| 50 | + V4 = "v4" |
| 51 | + """v4 (str): Fifth policy value.""" |
| 52 | + |
| 53 | + V5 = "v5" |
| 54 | + """v5 (str): Sixth policy value.""" |
| 55 | + |
| 56 | + |
| 57 | +class ExtendedAdapter(Adapter, FilteredAdapter): |
| 58 | + """ |
| 59 | + Extended Casbin adapter with filtering capabilities. |
| 60 | +
|
| 61 | + This adapter extends the base Django ORM Casbin adapter to support filtered |
| 62 | + policy loading, allowing for more efficient policy management by loading |
| 63 | + only relevant policy rules based on specified filter criteria. |
| 64 | +
|
| 65 | + Inherits from: |
| 66 | + Adapter: Base Django adapter for Casbin policy persistence. |
| 67 | + FilteredAdapter: Interface for filtered policy loading. |
| 68 | + """ |
| 69 | + |
| 70 | + def is_filtered(self) -> bool: |
| 71 | + """ |
| 72 | + Check if the adapter supports filtering. |
| 73 | +
|
| 74 | + Returns: |
| 75 | + bool: True if the adapter supports filtered policy loading, False otherwise. |
| 76 | + """ |
| 77 | + return True |
| 78 | + |
| 79 | + def load_filtered_policy(self, model: Model, filter: Filter) -> None: # pylint: disable=redefined-builtin |
| 80 | + """ |
| 81 | + Load policy rules from storage with filtering applied. |
| 82 | +
|
| 83 | + This method loads policy rules from the database and applies the specified |
| 84 | + filter to load only relevant rules. The filtered rules are then loaded |
| 85 | + into the provided Casbin model. |
| 86 | +
|
| 87 | + IMPORTANT: This method is used internally by the ``enforcer.load_filtered_policy()`` |
| 88 | + method. Do not call this method directly. If you need to load policy rules, use |
| 89 | + the ``enforcer.load_filtered_policy()`` method. |
| 90 | +
|
| 91 | + Args: |
| 92 | + model (Model): The Casbin model to load policy rules into. |
| 93 | + filter (Filter): Filter object containing criteria for policy selection. |
| 94 | + Should have attributes like ptype, v0, v1, etc. with lists |
| 95 | + of values to filter by. |
| 96 | + """ |
| 97 | + queryset = CasbinRule.objects.using(self.db_alias) |
| 98 | + filtered_queryset = self.filter_query(queryset, filter) |
| 99 | + for line in filtered_queryset: |
| 100 | + persist.load_policy_line(str(line), model) |
| 101 | + |
| 102 | + def filter_query(self, queryset: QuerySet, filter: Filter) -> QuerySet: # pylint: disable=redefined-builtin |
| 103 | + """ |
| 104 | + Apply filter criteria to the policy queryset. |
| 105 | +
|
| 106 | + This method takes a Django queryset of CasbinRule objects and applies |
| 107 | + filtering based on the provided filter object's attributes. It supports |
| 108 | + filtering by policy type (ptype) and policy values (v0-v5). |
| 109 | +
|
| 110 | + Args: |
| 111 | + queryset (QuerySet): Django queryset of CasbinRule objects to filter. |
| 112 | + filter (Filter): Filter object with attributes (ptype, v0, v1, v2, v3, v4, v5) |
| 113 | + containing lists of values to filter by. Empty lists are ignored. |
| 114 | +
|
| 115 | + Returns: |
| 116 | + QuerySet: Filtered and ordered queryset of CasbinRule objects. |
| 117 | + """ |
| 118 | + for attr in PolicyAttribute: |
| 119 | + filter_values = getattr(filter, attr.value) |
| 120 | + if len(filter_values) > 0: |
| 121 | + filter_kwargs = {f"{attr.value}__in": filter_values} |
| 122 | + queryset = queryset.filter(**filter_kwargs) |
| 123 | + return queryset.order_by("id") |
0 commit comments