Skip to content

Commit

Permalink
Disallow anonymous users to pull new data via pull-through distributions
Browse files Browse the repository at this point in the history
closes #1657
  • Loading branch information
lubosmj committed Jul 1, 2024
1 parent da8a70f commit bd3a5e1
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGES/1657.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Disallowed anonymous users to pull new content via a pull-through caching distribution. Content that
is already cached/downloaded can be still pulled.
20 changes: 15 additions & 5 deletions pulp_container/app/registry_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def get_pull_through_drv(self, path):
.order_by("-base_path")
.first()
)
if not pull_through_cache_distribution:
if not pull_through_cache_distribution or not self.request.user.is_authenticated:
raise RepositoryNotFound(name=path)

try:
Expand Down Expand Up @@ -1028,7 +1028,11 @@ def handle_safe_method(self, request, path, pk):
tag = models.Tag.objects.get(name=pk, pk__in=repository_version.content)
except models.Tag.DoesNotExist:
distribution = distribution.cast()
if distribution.remote and distribution.pull_through_distribution_id:
if (
distribution.remote
and distribution.pull_through_distribution_id
and request.user.is_authenticated
):
remote = distribution.remote.cast()
# issue a head request first to ensure that the content exists on the remote
# source; we want to prevent immediate "not found" error responses from
Expand Down Expand Up @@ -1068,13 +1072,19 @@ def handle_safe_method(self, request, path, pk):
manifest = models.Manifest.objects.get(digest=pk, pk__in=repository_version.content)
except models.Manifest.DoesNotExist:
repository = repository.cast()
# the manifest might be a part of listed manifests currently being uploaded
# or saved during the pull-through caching
try:
manifest = repository.pending_manifests.get(digest=pk)
manifest.touch()
except models.Manifest.DoesNotExist:
pass
if not (
distribution.remote
and distribution.pull_through_distribution_id
and request.user.is_authenticated
):
# the manifest might be a part of listed manifests currently being uploaded
# or saved during the pull-through caching; anonymous users are not allowed
# to trigger manifest downloading
raise ManifestNotFound(reference=pk)

distribution = distribution.cast()
if distribution.remote and distribution.pull_through_distribution_id:
Expand Down
2 changes: 1 addition & 1 deletion pulp_container/app/token_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def authenticate(self, request):
If basic authentication could not success, remote webserver authentication is considered.
"""
if request.headers.get("Authorization") == "Basic Og==":
return (AnonymousUser, None)
return (AnonymousUser(), None)

try:
user = super().authenticate(request)
Expand Down
10 changes: 10 additions & 0 deletions pulp_container/tests/functional/api/test_pull_through_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import subprocess
import pytest

from subprocess import CalledProcessError
from uuid import uuid4

from pulp_container.tests.functional.constants import (
Expand Down Expand Up @@ -32,6 +33,7 @@ def pull_through_distribution(
@pytest.fixture
def pull_and_verify(
add_to_cleanup,
anonymous_user,
container_pull_through_distribution_api,
container_distribution_api,
container_repository_api,
Expand All @@ -46,6 +48,10 @@ def _pull_and_verify(images, pull_through_distribution):
remote_image_path = f"{REGISTRY_V2}/{image_path}"
local_image_path = f"{pull_through_distribution.base_path}/{image_path}"

# 0. test if an anonymous user cannot pull new content through the pull-through cache
with anonymous_user, pytest.raises(CalledProcessError):
local_registry.pull(local_image_path)

# 1. pull remote content through the pull-through distribution
local_registry.pull(local_image_path)
local_image = local_registry.inspect(local_image_path)
Expand Down Expand Up @@ -89,6 +95,10 @@ def _pull_and_verify(images, pull_through_distribution):
tags = container_tag_api.list(repository_version=repository.latest_version_href).results
assert sorted(tags_to_verify) == sorted([tag.name for tag in tags])

# 6. test if the anonymous user can pull existing content via the pull-through cache
with anonymous_user:
local_registry.pull(local_image_path)

return _pull_and_verify


Expand Down
14 changes: 9 additions & 5 deletions pulp_container/tests/functional/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import requests
import subprocess

from contextlib import contextmanager
from contextlib import contextmanager, suppress
from urllib.parse import urljoin, urlparse
from uuid import uuid4

Expand Down Expand Up @@ -196,12 +196,16 @@ def pull(image_path):
registry_client.login(
"-u", bindings_cfg.username, "-p", bindings_cfg.password, registry_name
)

try:
registry_client.pull("/".join([registry_name, image_path]))
finally:
registry_client.logout(registry_name)
else:
registry_client.logout(registry_name)
try:
with suppress(subprocess.CalledProcessError):
registry_client.logout(registry_name)

registry_client.pull("/".join([registry_name, image_path]))
finally:
registry_client.logout(registry_name)

@staticmethod
def tag_and_push(image_path, local_url, *args):
Expand Down

0 comments on commit bd3a5e1

Please sign in to comment.