diff --git a/sync/models/base.py b/sync/models/base.py index 54a0efd..4593078 100644 --- a/sync/models/base.py +++ b/sync/models/base.py @@ -12,6 +12,12 @@ class BaseModel(models.Model): class Meta: abstract = True + def get_store_id(self): + if isinstance(self, SideStoreModel): + return self.store_id + else: + return None + def get_children_by_model(self): """ Returns a dictionary where: diff --git a/sync/signals.py b/sync/signals.py index 55a4303..7e0f093 100644 --- a/sync/signals.py +++ b/sync/signals.py @@ -1,4 +1,5 @@ from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed +from django.db import models from django.dispatch import receiver from .models import DataAccess, ModelLog, ModelOperation, BaseModel, SideStoreModel @@ -18,6 +19,39 @@ def synchronization_prepare(sender, instance, created=False, **kwargs): save_model_log_if_possible(instance, kwargs.get('signal'), created) +@receiver(pre_save) +def detect_foreign_key_changes(sender, instance, **kwargs): + if not hasattr(instance, 'pk') or not instance.pk: + return + if not isinstance(instance, BaseModel): + return + + data_access_list = related_data_access(instance) + if data_access_list: + try: + old_instance = sender.objects.get(pk=instance.pk) + except sender.DoesNotExist: + return + + # Check foreign key fields + for field in sender._meta.get_fields(): + if isinstance(field, models.ForeignKey): + old_value = getattr(old_instance, field.name, None) + new_value = getattr(instance, field.name, None) + if old_value != new_value: + for data_access in data_access_list: + if old_value: + model_name = old_value.__class__.__name__ + save_model_log(data_access.shared_with.all(), 'REVOKE_ACCESS', model_name, old_value.id, old_value.get_store_id()) + if new_value: + model_name = new_value.__class__.__name__ + save_model_log(data_access.shared_with.all(), 'GRANT_ACCESS', model_name, new_value.id, new_value.get_store_id()) + + # def save_model_log(users, model_operation, model_name, model_id, store_id): + # REVOKE access for old_value and GRANT new_value + print(f"Foreign key changed in {sender.__name__}: " + f"{field.name} from {old_value} to {new_value}") + @receiver([post_save, post_delete]) def synchronization_notifications(sender, instance, created=False, **kwargs): """ @@ -139,7 +173,7 @@ def related_users(instance): related_users = [ri.related_user for ri in related_instances if isinstance(ri, BaseModel)] users.update(related_users) - data_access_list = related_data_access(instance, related_instances) + data_access_list = instances_related_data_access(instance, related_instances) for data_access in data_access_list: users.add(data_access.related_user) users.update(data_access.shared_with.all()) @@ -149,7 +183,11 @@ def related_users(instance): return {user for user in users if user is not None} -def related_data_access(instance, related_instances): +def related_data_access(instance): + related_instances = instance.related_instances() + return instances_related_data_access(instance, related_instances) + +def instances_related_data_access(instance, related_instances): # related_instances = instance.related_instances() related_ids = [ri.id for ri in related_instances] related_ids.append(instance.id) diff --git a/sync/views.py b/sync/views.py index fd7eed5..5a0bce0 100644 --- a/sync/views.py +++ b/sync/views.py @@ -179,7 +179,6 @@ class SynchronizationApi(HierarchyApiView): self.add_parents_recursively(instance, grants) elif log.operation == 'REVOKE_ACCESS': print(f'revoke access {log.model_id} - {log.store_id}') - # Add to revocations instead of deletions revocations[log.model_name].append({ 'model_id': log.model_id, 'store_id': log.store_id