Эх сурвалжийг харах

[doc2] Validate against creating circular dependency when saving and moving docs

Jenny Kim 9 жил өмнө
parent
commit
4fb2aa0

+ 31 - 0
desktop/core/src/desktop/models.py

@@ -1067,6 +1067,11 @@ class Document2(models.Model):
           Document2.objects.filter(name=self.name, owner=self.owner, type='directory').exists():
       raise FilesystemException(_('Cannot create or modify directory with name: %s') % self.name)
 
+    # Validate that parent directory does not create cycle
+    if self._contains_cycle():
+      raise FilesystemException(_('Cannot save document %s under parent directory %s due to circular dependency') %
+                                (self.name, self.parent_directory.uuid))
+
   def move(self, directory, user):
     if not directory.is_directory:
       raise FilesystemException(_('Target with UUID %s is not a directory') % directory.uuid)
@@ -1171,6 +1176,32 @@ class Document2(models.Model):
             snippet['is_redacted'] = True
       self.data = json.dumps(data_dict)
 
+  def _contains_cycle(self):
+    """
+    Uses Floyd's cycle-detection algorithm to detect a cycle (aka Tortoise and Hare)
+    https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare
+    """
+    slow = self
+    fast = self
+    while True:
+      slow = slow.parent_directory
+      if slow and slow.uuid == self.uuid:
+        slow = self
+
+      if fast.parent_directory is not None:
+        if fast.parent_directory.uuid == self.uuid:
+          fast = self.parent_directory.parent_directory
+        else:
+          fast = fast.parent_directory.parent_directory
+      else:
+        return False
+
+      if slow is None or fast is None:
+        return False
+
+      if slow == fast:
+        return True
+
 
 class DirectoryManager(Document2Manager):
 

+ 17 - 0
desktop/core/src/desktop/tests_doc2.py

@@ -345,6 +345,23 @@ class TestDocument2(object):
     assert_equal('Cannot create or modify directory with name: .Trash', data['message'])
 
 
+  def test_validate_circular_directory(self):
+    # Test that saving a document with cycle raises an error, i.e. - This should fail:
+    # a.parent_directory = b
+    # b.parent_directory = c
+    # c.parent_directory = a
+    c_dir = Directory.objects.create(name='c', owner=self.user)
+    b_dir = Directory.objects.create(name='b', owner=self.user, parent_directory=c_dir)
+    a_dir = Directory.objects.create(name='a', owner=self.user, parent_directory=b_dir)
+    response = self.client.post('/desktop/api2/doc/move', {
+        'source_doc_uuid': json.dumps(c_dir.uuid),
+        'destination_doc_uuid': json.dumps(a_dir.uuid)
+    })
+    data = json.loads(response.content)
+    assert_equal(-1, data['status'], data)
+    assert_true('circular dependency' in data['message'], data)
+
+
 class TestDocument2Permissions(object):
 
   def setUp(self):