1515"""
1616
1717import logging
18- from contextlib import contextmanager
1918
2019logger = logging .getLogger ('ansible_base.rbac.sync' )
2120
2221
23- def reverse_sync_enabled_all_conditions (assignment ):
22+ def reverse_sync_enabled_all_conditions (instance ):
2423 """This checks for basically all cases we do not reverse sync
25- 1. object level flag for skipping the sync
26- 2. environment variable to skip sync
27- 3. context manager to disable sync
28- 4. RESOURCE_SERVER setting not actually set
24+ 1. global reverse sync enabled flag (including context manager)
25+ 2. RESOURCE_SERVER setting not actually set
26+ 3. environment variable to skip sync
27+ 4. object level flag for skipping the sync
2928 """
3029 from ansible_base .resource_registry .apps import _should_reverse_sync
3130 from ansible_base .resource_registry .signals .handlers import reverse_sync_enabled
3231 from ansible_base .resource_registry .utils .sync_to_resource_server import should_skip_reverse_sync
3332
34- if not _should_reverse_sync () :
33+ if not reverse_sync_enabled . enabled :
3534 return False
3635
37- if not reverse_sync_enabled . enabled :
36+ if not _should_reverse_sync () :
3837 return False
3938
40- if should_skip_reverse_sync (assignment ):
41- return
39+ if should_skip_reverse_sync (instance ):
40+ return False
4241
4342 return True
4443
@@ -63,86 +62,24 @@ def maybe_reverse_sync_unassignment(role_definition, actor, content_object):
6362 client .sync_unassignment (role_definition , actor , content_object )
6463
6564
66- def _should_reverse_sync (instance ) -> bool :
67- """Check if reverse sync should be performed for the given instance .
65+ def maybe_reverse_sync_role_definition (instance , action = "update" ) :
66+ """Manually trigger reverse- sync for a RoleDefinition if appropriate .
6867
69- This checks the same conditions as the signal handler but without
70- relying on the global reverse_sync_enabled state being temporarily disabled.
71- """
72- from ansible_base .resource_registry .apps import _should_reverse_sync as _global_should_sync
73- from ansible_base .resource_registry .utils .sync_to_resource_server import should_skip_reverse_sync
74-
75- # Check global RESOURCE_SERVER configuration
76- if not _global_should_sync ():
77- return False
78-
79- # Check instance-specific skip conditions
80- if should_skip_reverse_sync (instance ):
81- return False
82-
83- return True
84-
85-
86- class DelayedReverseSyncContext :
87- """Context class for delayed reverse sync operations."""
88-
89- def __init__ (self , action = "update" ):
90- self .action = action
91- self .instance = None
92-
93- def set_instance (self , instance ):
94- """Set the instance to sync after the context exits."""
95- self .instance = instance
96-
97-
98- @contextmanager
99- def delayed_reverse_sync (instance , action = "update" ):
100- """Context manager that disables reverse-sync during execution, then manually syncs afterward.
101-
102- This is useful for operations where the instance needs to be fully constructed
103- (including many-to-many relationships) before syncing to the resource server.
104-
105- This solves the timing issue where post_save fires before many-to-many
106- relations are saved, causing empty permissions to be synced.
68+ This should be called after the instance is fully constructed with
69+ all many-to-many relationships saved.
10770
10871 Args:
109- instance: The Django model instance to sync (can be None for create operations)
72+ instance: The RoleDefinition instance to sync
11073 action: The action type ("create" or "update")
111-
112- Usage:
113- # For updates
114- with delayed_reverse_sync(role_def, "update"):
115- # Perform operations that modify the instance
116- role_def.permissions.set(permissions)
117- # Sync happens here if appropriate
118-
119- # For creates
120- ctx = delayed_reverse_sync(None, "create")
121- with ctx:
122- instance = serializer.save()
123- ctx.set_instance(instance)
124- # Sync happens here if appropriate
12574 """
126- from ansible_base .resource_registry .signals .handlers import no_reverse_sync , sync_to_resource_server_post_save
127-
128- # Create a context object that can be updated
129- context = DelayedReverseSyncContext (action )
130- context .instance = instance
131-
132- with no_reverse_sync ():
133- yield context
134-
135- # After the context, check if we should sync and do it manually
136- final_instance = context .instance
137- if final_instance and _should_reverse_sync (final_instance ):
138- logger .debug (f"Performing delayed reverse-sync for { final_instance } (action: { action } )" )
139- # Use the same logic as the post_save signal handler
140- created = (action == "create" )
141- sync_to_resource_server_post_save (
142- sender = type (final_instance ),
143- instance = final_instance ,
144- created = created ,
145- update_fields = None
146- )
147- else :
148- logger .debug (f"Skipping delayed reverse-sync for { final_instance } (action: { action } )" )
75+ if not reverse_sync_enabled_all_conditions (instance ):
76+ logger .debug (f"Skipping reverse-sync for { instance } (action: { action } )" )
77+ return
78+
79+ logger .debug (f"Performing reverse-sync for { instance } (action: { action } )" )
80+
81+ # Use the same logic as the post_save signal handler
82+ from ansible_base .resource_registry .signals .handlers import sync_to_resource_server_post_save
83+
84+ created = action == "create"
85+ sync_to_resource_server_post_save (sender = type (instance ), instance = instance , created = created , update_fields = None )
0 commit comments