1616"""
1717
1818import logging
19+ from uuid import uuid4
1920
2021from casbin import SyncedEnforcer
2122from casbin_adapter .enforcer import initialize_enforcer
2223from django .conf import settings
2324
2425from openedx_authz .engine .adapter import ExtendedAdapter
26+ from openedx_authz .models .engine import PolicyCacheControl
2527
2628
2729def libraries_v2_enabled () -> bool :
@@ -68,6 +70,7 @@ class AuthzEnforcer:
6870
6971 _enforcer = None
7072 _adapter = None
73+ _last_policy_loaded_version = None
7174
7275 def __new__ (cls ):
7376 """Singleton pattern to ensure a single enforcer instance."""
@@ -153,6 +156,45 @@ def configure_enforcer_auto_save_and_load(cls):
153156
154157 cls .configure_enforcer_auto_save (auto_save_policy )
155158
159+ @classmethod
160+ def load_policy_if_needed (cls ):
161+ """Load policy if the last load version indicates it's needed.
162+
163+ This method checks if the policy needs to be reloaded comparing
164+ the last load version with the version in the cache invalidation model,
165+ and reloads it if necessary.
166+
167+ Returns:
168+ None
169+ """
170+ last_version = PolicyCacheControl .get_version ()
171+
172+ if last_version is None :
173+ # No version in cache control; initialize it
174+ last_version = uuid4 ()
175+ PolicyCacheControl .set_version (last_version )
176+ logger .info ("Initialized policy last modified version in cache control." )
177+
178+ if cls ._last_policy_loaded_version is None or last_version != cls ._last_policy_loaded_version :
179+ # Policy has been modified since last load; reload it
180+ cls ._enforcer .load_policy ()
181+ cls ._last_policy_loaded_version = last_version
182+ logger .info (f"Reloaded policy to version { last_version } " )
183+
184+ @classmethod
185+ def invalidate_policy_cache (cls ):
186+ """Invalidate the current policy cache to force a reload on next check.
187+
188+ This method updates the last modified version in the cache invalidation model
189+ to a new UUID, indicating that the policy has changed.
190+
191+ Returns:
192+ None
193+ """
194+ new_version = uuid4 ()
195+ PolicyCacheControl .set_version (new_version )
196+ logger .info (f"Invalidated policy cache to version { new_version } " )
197+
156198 @classmethod
157199 def get_enforcer (cls ) -> SyncedEnforcer :
158200 """Get the enforcer instance, creating it if needed.
@@ -163,6 +205,9 @@ def get_enforcer(cls) -> SyncedEnforcer:
163205 if cls ._enforcer is None :
164206 cls ._enforcer = cls ._initialize_enforcer ()
165207
208+ # (re)load policy if needed
209+ cls .load_policy_if_needed ()
210+
166211 # HACK: This code block will only be useful when in Ulmo to deactivate
167212 # the enforcer when the new library experience is disabled. It should be
168213 # removed for the next release cycle.
0 commit comments