Skip to content

Commit

Permalink
Add functions to validate file path overlaps
Browse files Browse the repository at this point in the history
  • Loading branch information
David Davis committed Nov 26, 2019
1 parent 2a5fb16 commit aa6076d
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES/5559.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add checks to prevent artifacts from having overlapping base paths in repo versions and publications.
52 changes: 52 additions & 0 deletions pulpcore/app/models/publication.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from gettext import gettext as _

from django.db import IntegrityError, models, transaction
from django.db.models import Q

from .base import MasterModel, Model
from .content import Artifact, Content, ContentArtifact
Expand Down Expand Up @@ -107,6 +110,49 @@ def delete(self, **kwargs):
CreatedResource.objects.filter(object_id=self.pk).delete()
super().delete(**kwargs)

def validate_path_overlap(self):
"""
Validate whether any artifact relative paths overlap (e.g. a/b and a/b/c). This checks
published artifacts, published metadata, and repo version content (if this publication is a
pass_through publication).
Plugins can override this method if they wish to provide their own validate_path_overlap
functionality or if they wish to skip this validation entirely.
Raises:
ValueError: If two artifact relative paths overlap
"""
q = Q()
for pa in self.published_artifact.all():
q |= Q(relative_path__startswith=f"{pa.relative_path}/")

for pm in self.published_metadata.all():
q |= Q(relative_path__startswith=f"{pm.relative_path}/")

if self.pass_through:
c_artifacts = ContentArtifact.objects. \
filter(content__pk__in=self.repository_version.content)
for ca in c_artifacts:
q |= Q(relative_path__startswith=f"{ca.relative_path}/")

conflicts = c_artifacts.filter(q).values_list("content__pk", flat=True)
if conflicts:
content_pks = [str(c) for c in conflicts]
raise ValueError(_("Cannot create publication due to file that overlaps path for "
"Content: {content}").format(content=(", ").join(content_pks)))

pa_conflicts = self.published_artifact.filter(q).values_list("pk", flat=True)
if pa_conflicts:
pa_pks = [str(pk) for pk in pa_conflicts]
raise ValueError(_("Cannot create publication due to file that overlaps path for "
"PublishedArtifact: {pa}").format(pa=(", ").join(pa_pks)))

pm_conflicts = self.published_metadata.filter(q).values_list("pk", flat=True)
if pm_conflicts:
pm_pks = [str(pk) for pk in pm_conflicts]
raise ValueError(_("Cannot create publication due to file that overlaps path for "
"PublishedMetadata: {pm}").format(pm=(", ").join(pm_pks)))

def __enter__(self):
"""
Enter context.
Expand All @@ -125,6 +171,12 @@ def __exit__(self, exc_type, exc_val, exc_tb):
exc_val (Exception): (optional) Instance of exception raised.
exc_tb (types.TracebackType): (optional) stack trace.
"""
try:
self.validate_path_overlap()
except ValueError:
self.delete()
raise

if not exc_val:
self.complete = True
self.save()
Expand Down
23 changes: 21 additions & 2 deletions pulpcore/app/models/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@

import django
from django.db import models, transaction
from django.db.models import Q
from django.urls import reverse

from pulpcore.app.util import get_view_name_for_model
from pulpcore.download.factory import DownloaderFactory
from pulpcore.exceptions import ResourceImmutableError

from .base import MasterModel, Model
from .content import Artifact, Content
from .content import Artifact, Content, ContentArtifact
from .task import CreatedResource, Task


Expand Down Expand Up @@ -128,7 +129,7 @@ def finalize_new_version(self, new_version):
Returns:
"""
pass
new_version.validate_path_overlap()

def latest_version(self):
"""
Expand Down Expand Up @@ -654,6 +655,24 @@ def _compute_counts(self):
counts_list.append(count_obj)
RepositoryVersionContentDetails.objects.bulk_create(counts_list)

def validate_path_overlap(self):
"""
Validate whether any artifact relative paths overlap (e.g. a/b and a/b/c)
Raises:
ValueError: If two artifact relative paths overlap
"""
q = Q()
c_artifacts = ContentArtifact.objects.filter(content__pk__in=self.content)
for ca in c_artifacts:
q |= Q(relative_path__startswith=f"{ca.relative_path}/")

conflicts = c_artifacts.filter(q).values_list("content__pk", flat=True)
if conflicts:
content_pks = [str(pk) for pk in conflicts]
raise ValueError(_("Cannot create repo version due to file that overlaps path for "
"Content: {content}").format(content=(", ".join(content_pks))))

def __enter__(self):
"""
Create the repository version
Expand Down

0 comments on commit aa6076d

Please sign in to comment.