Skip to content

Commit

Permalink
Add validation to check for file path overlaps
Browse files Browse the repository at this point in the history
  • Loading branch information
David Davis committed Nov 27, 2019
1 parent 2a5fb16 commit ca604a8
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGES/5559.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add checks to prevent artifacts from having overlapping base paths in repo versions and
publications.
4 changes: 4 additions & 0 deletions CHANGES/plugin_api/5559.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Added artifact path overlap checks for repo versions and publications. Plugins should call
``super().finalize_new_version(..)`` or ``validate_path_overlap()`` on RepositoryVersion to get the
check for repo versions. For publications, plugins can override ``validate_path_overlap()`` on
Publication to skip or customize the validation.
37 changes: 37 additions & 0 deletions pulpcore/app/models/publication.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import re
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 +111,33 @@ 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
"""
paths = []
paths += self.published_artifact.values_list("relative_path", flat=True)
paths += self.published_metadata.values_list("relative_path", flat=True)

if self.pass_through:
paths += ContentArtifact.objects. \
filter(content__pk__in=self.repository_version.content). \
values_list("relative_path", flat=True)

for path in paths:
matches = [p for p in paths if re.match(f"^{path}/.*", p)]
if matches:
raise ValueError(_("Publication invalid as file path '{path}' overlaps with: "
"{matches}.").format(path=path, matches=(", ").join(matches)))

def __enter__(self):
"""
Enter context.
Expand All @@ -125,6 +156,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
25 changes: 22 additions & 3 deletions pulpcore/app/models/repository.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
"""
Repository related Django models.
"""
import logging
import re
from contextlib import suppress
from gettext import gettext as _
from os import path
import logging

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 +130,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 +656,23 @@ 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
"""
paths = ContentArtifact.objects. \
filter(content__pk__in=self.content). \
values_list("relative_path", flat=True)

for rpath in paths:
matches = [p for p in paths if re.match(f"^{rpath}/.*", p)]
if matches:
raise ValueError(_("Repository version cannot be saved as file path '{path}' overlaps with: "
"{matches}.").format(path=rpath, matches=(", ").join(matches)))

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

0 comments on commit ca604a8

Please sign in to comment.