Skip to content

Commit

Permalink
Add view layer logic to prevent overlapping distribution paths
Browse files Browse the repository at this point in the history
  • Loading branch information
David Davis authored and daviddavis committed Apr 18, 2018
1 parent d26baec commit 4fc05cf
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 2 deletions.
28 changes: 26 additions & 2 deletions pulpcore/pulpcore/app/serializers/repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from gettext import gettext as _

from django.core import validators
from django.db.models import Q

from rest_framework import serializers
from rest_framework.validators import UniqueValidator
Expand Down Expand Up @@ -203,7 +204,7 @@ class DistributionSerializer(ModelSerializer):
)
base_path = serializers.CharField(
help_text=_('The base (relative) path component of the published url. Avoid paths that \
overlap with other distributor base paths (e.g. "foo" and "foo/bar")'),
overlap with other distribution base paths (e.g. "foo" and "foo/bar")'),
validators=[validators.MaxLengthValidator(
models.Distribution._meta.get_field('base_path').max_length,
message=_('Distribution base_path length must be less than {} characters').format(
Expand Down Expand Up @@ -244,8 +245,31 @@ class Meta:
'name', 'base_path', 'publisher', 'publication', 'base_url', 'repository',
)

def _validate_path_overlap(self, path):
# look for any base paths nested in path
search = path.split("/")[0]
q = Q(base_path=search)
for subdir in path.split("/")[1:]:
search = "/".join((search, subdir))
q |= Q(base_path=search)

# look for any base paths that nest path
q |= Q(base_path__startswith='{}/'.format(path))
qs = models.Distribution.objects.filter(q)

if self.instance is not None:
qs = qs.exclude(pk=self.instance.pk)

match = qs.first()
if match:
raise serializers.ValidationError(detail=_("Overlaps with existing distribution '"
"{}'").format(match.name))

return path

def validate_base_path(self, path):
return self._validate_relative_path(path)
self._validate_relative_path(path)
return self._validate_path_overlap(path)

def validate(self, data):
if 'publisher' in data:
Expand Down
59 changes: 59 additions & 0 deletions pulpcore/tests/serializers/test_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from unittest import TestCase

from pulpcore.app.models import Distribution
from pulpcore.app.serializers import DistributionSerializer


class TestDistributionPath(TestCase):
def test_overlap(self):
Distribution.objects.create(base_path="foo/bar", name="foobar")
overlap_errors = {'base_path': ["Overlaps with existing distribution 'foobar'"]}

# test that the new distribution cannot be nested in an existing path
data = {"name": "foobarbaz", "base_path": "foo/bar/baz"}
serializer = DistributionSerializer(data=data)
self.assertFalse(serializer.is_valid())
self.assertDictEqual(overlap_errors, serializer.errors)

# test that the new distribution cannot nest an existing path
data = {"name": "foo", "base_path": "foo"}
serializer = DistributionSerializer(data=data)
self.assertFalse(serializer.is_valid())
self.assertDictEqual(overlap_errors, serializer.errors)

def test_no_overlap(self):
Distribution.objects.create(base_path="fu/bar", name="fubar")

# different path
data = {"name": "fufu", "base_path": "fubar"}
serializer = DistributionSerializer(data=data)
self.assertTrue(serializer.is_valid())
self.assertDictEqual({}, serializer.errors)

# common base path but different path
data = {"name": "fufu", "base_path": "fu/baz"}
serializer = DistributionSerializer(data=data)
self.assertTrue(serializer.is_valid())
self.assertDictEqual({}, serializer.errors)

def test_slashes(self):
overlap_errors = {'base_path': ["Relative path cannot begin or end with slashes."]}

data = {"name": "fefe", "base_path": "fefe/"}
serializer = DistributionSerializer(data=data)
self.assertFalse(serializer.is_valid())
self.assertDictEqual(overlap_errors, serializer.errors)

data = {"name": "fefe", "base_path": "/fefe/foo"}
serializer = DistributionSerializer(data=data)
self.assertFalse(serializer.is_valid())
self.assertDictEqual(overlap_errors, serializer.errors)

def test_uniqueness(self):
Distribution.objects.create(base_path="fizz/buzz", name="fizzbuzz")
data = {"name": "feefee", "base_path": "fizz/buzz"}
overlap_errors = {'base_path': ["This field must be unique."]}

serializer = DistributionSerializer(data=data)
self.assertFalse(serializer.is_valid())
self.assertDictEqual(overlap_errors, serializer.errors)

0 comments on commit 4fc05cf

Please sign in to comment.