Skip to content

Commit ddedad9

Browse files
AlanCodingdmzoneill
authored andcommitted
Flip default of reverse sync to True
Thow an error if shared fields are edited with local management turned off Fix grammar error Co-authored-by: Rick Elrod <[email protected]> Add test and change to ValidationError Disconnect signals to fix failure
1 parent 7c71771 commit ddedad9

File tree

2 files changed

+79
-2
lines changed

2 files changed

+79
-2
lines changed

ansible_base/resource_registry/utils/sync_to_resource_server.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ def sync_to_resource_server(instance, action, ansible_id=None):
5555
logger.info(f"Skipping sync of resource {instance} because its service_id is local")
5656
return
5757

58+
# By this point we have passed the exit conditions
59+
# If an edit requires a resource sync, but we intended to not allow local edits
60+
# that has led to a contradiction, normally this is enforced by the service API
61+
# but for direct ORM actions we throw an error as a last-resort safety measure
62+
if not getattr(settings, "ALLOW_LOCAL_RESOURCE_MANAGEMENT"):
63+
raise ValidationError(
64+
"Configured to resource sync, which conflicts with ALLOW_LOCAL_RESOURCE_MANAGEMENT. "
65+
"Try setting environment variable ANSIBLE_REVERSE_RESOURCE_SYNC to false."
66+
)
67+
5868
user_ansible_id = None
5969
user = get_current_user()
6070
if user:

test_app/tests/resource_registry/test_utils.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import os
22
import uuid
3-
from contextlib import nullcontext
3+
from contextlib import contextmanager, nullcontext
44
from unittest import mock
55

66
import pytest
77
from crum import impersonate
88
from django.contrib.auth.models import AnonymousUser
99
from django.db import IntegrityError, connection
10-
from django.test.utils import CaptureQueriesContext
10+
from django.test.utils import CaptureQueriesContext, override_settings
1111
from rest_framework.exceptions import ValidationError
1212

1313
from ansible_base.resource_registry import apps
14+
from ansible_base.resource_registry.models import Resource
1415
from ansible_base.resource_registry.signals.handlers import no_reverse_sync
1516
from ansible_base.resource_registry.utils.sync_to_resource_server import sync_to_resource_server
1617
from test_app.models import Organization
@@ -246,3 +247,69 @@ def test_should_reverse_sync(self, settings, new_settings, should_sync):
246247
setattr(settings, key, value)
247248

248249
assert apps._should_reverse_sync() == should_sync
250+
251+
252+
@pytest.mark.django_db
253+
class TestConflitingSyncSettings:
254+
CONFLICT_SETTINGS = dict(
255+
ALLOW_LOCAL_RESOURCE_MANAGEMENT=True,
256+
RESOURCE_SERVER_SYNC_ENABLED=True,
257+
RESOURCE_SERVER={'URL': 'https://foo.invalid', 'SECRET_KEY': 'abcdefghijklmnopqrstuvwxyz'},
258+
)
259+
260+
@contextmanager
261+
def apply_settings(self):
262+
with override_settings(**self.CONFLICT_SETTINGS):
263+
apps.connect_resource_signals(None)
264+
yield
265+
apps.disconnect_resource_signals(None)
266+
apps.connect_resource_signals(None)
267+
268+
def mark_external(self, obj):
269+
"Make object appears as if managed by another system"
270+
if hasattr(obj, '_skip_reverse_resource_sync'):
271+
delattr(obj, '_skip_reverse_resource_sync')
272+
res = Resource.get_resource_for_object(obj)
273+
res.service_id = uuid.uuid4()
274+
res.save()
275+
276+
def test_block_org_save(self, organization):
277+
with self.apply_settings():
278+
organization.save() # does not error
279+
self.mark_external(organization)
280+
with pytest.raises(ValidationError):
281+
organization.name += 'foo'
282+
organization.save()
283+
284+
def test_block_org_deletion(self, organization):
285+
with self.apply_settings():
286+
organization.save() # create resource entry
287+
self.mark_external(organization)
288+
with pytest.raises(ValidationError):
289+
organization.delete()
290+
291+
def test_change_non_synced_field(self, admin_user):
292+
with self.apply_settings():
293+
admin_user.save() # create resource entry
294+
self.mark_external(admin_user)
295+
assert admin_user.is_superuser is True
296+
admin_user.is_superuser = False
297+
admin_user.save() # does not error
298+
299+
def test_change_user_synced_field(self, admin_user):
300+
with self.apply_settings():
301+
admin_user.save() # create resource entry
302+
self.mark_external(admin_user)
303+
with pytest.raises(ValidationError):
304+
admin_user.username = 'some_other_username'
305+
admin_user.save()
306+
307+
def test_create_shared_resource(self):
308+
with self.apply_settings():
309+
with pytest.raises(ValidationError):
310+
Organization.objects.create(name='new org')
311+
312+
def test_create_allowed_no_sync(self):
313+
with self.apply_settings():
314+
with no_reverse_sync():
315+
Organization.objects.create(name='new org')

0 commit comments

Comments
 (0)