Repository: translate/pootle

Issue:
Store.create should not allow /s in the Store.name
atm its possible to create a Store that has `/` in the name - which foos with the directory system



Discussion:

Resolving patch:
diff --git a/pootle/apps/pootle_store/migrations/0015_add_slashes_validator_for_name.py b/pootle/apps/pootle_store/migrations/0015_add_slashes_validator_for_name.py
new file mode 100644
--- /dev/null
+++ b/pootle/apps/pootle_store/migrations/0015_add_slashes_validator_for_name.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import pootle_store.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('pootle_store', '0014_add_unit_index_togethers'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='store',
+            name='name',
+            field=models.CharField(max_length=128, editable=False, validators=[pootle_store.models.validate_no_slashes]),
+        ),
+    ]
diff --git a/pootle/apps/pootle_store/migrations/0016_blank_last_sync_revision.py b/pootle/apps/pootle_store/migrations/0016_blank_last_sync_revision.py
new file mode 100644
--- /dev/null
+++ b/pootle/apps/pootle_store/migrations/0016_blank_last_sync_revision.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('pootle_store', '0015_add_slashes_validator_for_name'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='store',
+            name='last_sync_revision',
+            field=models.IntegerField(db_index=True, null=True, blank=True),
+        ),
+    ]
diff --git a/pootle/apps/pootle_store/models.py b/pootle/apps/pootle_store/models.py
--- a/pootle/apps/pootle_store/models.py
+++ b/pootle/apps/pootle_store/models.py
@@ -18,6 +18,7 @@
 
 from django.conf import settings
 from django.contrib.auth import get_user_model
+from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
 from django.core.validators import MinValueValidator
 from django.db import models
@@ -1115,6 +1116,14 @@ def get_last_updated_info(self):
 # # # # # # # # # # #  Store # # # # # # # # # # # # # #
 
 
+def validate_no_slashes(value):
+    if '/' in value:
+        raise ValidationError('Store name cannot contain "/" characters')
+
+    if '\\' in value:
+        raise ValidationError('Store name cannot contain "\\" characters')
+
+
 # Needed to alter storage location in tests
 fs = PootleFileSystemStorage()
 
@@ -1152,14 +1161,16 @@ class Store(models.Model, CachedTreeItem, base.TranslationStore):
                                    db_index=True, verbose_name=_("Path"))
     # any changes to the `name` field may require updating the schema
     # see migration 0007_case_sensitive_schema.py
-    name = models.CharField(max_length=128, null=False, editable=False)
+    name = models.CharField(max_length=128, null=False, editable=False,
+                            validators=[validate_no_slashes])
 
     file_mtime = models.DateTimeField(default=datetime_min)
     state = models.IntegerField(null=False, default=NEW, editable=False,
                                 db_index=True)
     creation_time = models.DateTimeField(auto_now_add=True, db_index=True,
                                          editable=False, null=True)
-    last_sync_revision = models.IntegerField(db_index=True, null=True)
+    last_sync_revision = models.IntegerField(db_index=True, null=True,
+                                             blank=True)
     obsolete = models.BooleanField(default=False)
 
     objects = StoreManager()
@@ -1228,6 +1239,9 @@ def save(self, *args, **kwargs):
 
         self.pootle_path = self.parent.pootle_path + self.name
 
+        # Force validation of fields.
+        self.full_clean()
+
         super(Store, self).save(*args, **kwargs)
         if created:
             store_log(user='system', action=STORE_ADDED,


Test patch:
diff --git a/tests/models/store.py b/tests/models/store.py
--- a/tests/models/store.py
+++ b/tests/models/store.py
@@ -21,6 +21,7 @@
 
 from translate.storage.factory import getclass
 
+from django.core.exceptions import ValidationError
 from django.core.files.uploadedfile import SimpleUploadedFile
 
 from pootle.core.delegate import (
@@ -878,6 +879,19 @@ def test_store_create(tp0):
             translation_project=tp)
 
 
+@pytest.mark.django_db
+def test_store_create_name_with_slashes_or_backslashes(tp0):
+    """Test Stores are not created with (back)slashes on their name."""
+
+    with pytest.raises(ValidationError):
+        Store.objects.create(name="slashed/name.po", parent=tp0.directory,
+                             translation_project=tp0)
+
+    with pytest.raises(ValidationError):
+        Store.objects.create(name="backslashed\\name.po", parent=tp0.directory,
+                             translation_project=tp0)
+
+
 @pytest.mark.django_db
 def test_store_get_file_class():
     store = Store.objects.filter(