Skip to content

Commit

Permalink
Add initial support for domains
Browse files Browse the repository at this point in the history
  • Loading branch information
gerrod3 committed Jan 22, 2025
1 parent 88b2002 commit 29b5466
Show file tree
Hide file tree
Showing 17 changed files with 341 additions and 154 deletions.
2 changes: 2 additions & 0 deletions CHANGES/domain-enablement.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add partial support for Domains. The plugin can be installed with the feature turned on, but it
only functions within the default domain.
1 change: 1 addition & 0 deletions pulp_container/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class PulpContainerPluginAppConfig(PulpPluginAppConfig):
label = "container"
version = "2.23.0.dev"
python_package_name = "pulp-container"
domain_compatible = True

def ready(self):
super().ready()
Expand Down
41 changes: 28 additions & 13 deletions pulp_container/app/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

from pulpcore.plugin.util import get_domain
from pulp_container.app.models import (
ContainerDistribution,
ContainerNamespace,
Expand Down Expand Up @@ -209,10 +210,10 @@ def generate_claim_set(issuer, issued_at, subject, audience, access):
}


def get_pull_through_distribution(path):
def get_pull_through_distribution(path, domain):
return (
ContainerPullThroughDistribution.objects.annotate(path=Value(path))
.filter(path__startswith=F("base_path"))
.filter(pulp_domain=domain, path__startswith=F("base_path"))
.order_by("-base_path")
.first()
)
Expand All @@ -231,6 +232,7 @@ def has_permission(self, obj, method, action, data):
request.method = method
request.user = self.user
request._full_data = data
request.pulp_domain = get_domain()
# Fake the corresponding view
view = FakeViewWithSerializer(action, lambda: obj)
return self.access_policy.has_permission(request, view)
Expand All @@ -239,50 +241,63 @@ def has_pull_permissions(self, path):
"""
Check if the user has permissions to pull from the repository specified by the path.
"""
domain = get_domain()
try:
distribution = ContainerDistribution.objects.get(base_path=path)
distribution = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
except ContainerDistribution.DoesNotExist:
namespace_name = path.split("/")[0]
try:
namespace = ContainerNamespace.objects.get(name=namespace_name)
namespace = ContainerNamespace.objects.get(name=namespace_name, pulp_domain=domain)
except ContainerNamespace.DoesNotExist:
# Check if the user is allowed to create a new namespace
return self.has_permission(None, "POST", "create", {"name": namespace_name})
return self.has_permission(
None, "POST", "create", {"name": namespace_name, "pulp_domain": domain}
)

if pt_distribution := get_pull_through_distribution(path):
if pt_distribution := get_pull_through_distribution(path, domain):
# Check if the user is allowed to create a new distribution
return self.has_pull_through_new_distribution_permissions(pt_distribution)
else:
# Check if the user is allowed to view distributions in the namespace
return self.has_permission(
namespace, "GET", "view_distribution", {"name": namespace_name}
namespace,
"GET",
"view_distribution",
{"name": namespace_name, "pulp_domain": domain},
)

if pt_distribution := get_pull_through_distribution(path):
if get_pull_through_distribution(path, domain):
# Check if the user is allowed to pull new content via a pull-through distribution
if self.has_pull_through_permissions(distribution):
return True

# Check if the user has general pull permissions
return self.has_permission(distribution, "GET", "pull", {"base_path": path})
return self.has_permission(
distribution, "GET", "pull", {"base_path": path, "pulp_domain": domain}
)

def has_push_permissions(self, path):
"""
Check if the user has permissions to push to the repository specified by the path.
"""
domain = get_domain()
try:
distribution = ContainerDistribution.objects.get(base_path=path)
distribution = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
except ContainerDistribution.DoesNotExist:
namespace_name = path.split("/")[0]
try:
namespace = ContainerNamespace.objects.get(name=namespace_name)
namespace = ContainerNamespace.objects.get(name=namespace_name, pulp_domain=domain)
except ContainerNamespace.DoesNotExist:
# Check if user is allowed to create a new namespace
return self.has_permission(None, "POST", "create", {"name": namespace_name})
return self.has_permission(
None, "POST", "create", {"name": namespace_name, "pulp_domain": domain}
)
# Check if user is allowed to create a new distribution in the namespace
return self.has_permission(namespace, "POST", "create_distribution", {})

return self.has_permission(distribution, "POST", "push", {"base_path": path})
return self.has_permission(
distribution, "POST", "push", {"base_path": path, "pulp_domain": domain}
)

def has_view_catalog_permissions(self, path):
"""
Expand Down
8 changes: 5 additions & 3 deletions pulp_container/app/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.db.models import F, Value

from pulpcore.plugin.cache import CacheKeys, AsyncContentCache, SyncContentCache
from pulpcore.plugin.util import get_domain, cache_key

from pulp_container.app.models import ContainerDistribution, ContainerPullThroughDistribution
from pulp_container.app.exceptions import RepositoryNotFound
Expand Down Expand Up @@ -65,16 +66,17 @@ def find_base_path_cached(request, cached):
"""
path = request.resolver_match.kwargs["path"]
path_exists = cached.exists(base_key=path)
path_exists = cached.exists(base_key=cache_key(path))
if path_exists:
return path
else:
domain = get_domain()
try:
distro = ContainerDistribution.objects.get(base_path=path)
distro = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
except ObjectDoesNotExist:
distro = (
ContainerPullThroughDistribution.objects.annotate(path=Value(path))
.filter(path__startswith=F("base_path"))
.filter(path__startswith=F("base_path"), pulp_domain=domain)
.order_by("-base_path")
.first()
)
Expand Down
7 changes: 5 additions & 2 deletions pulp_container/app/content.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from aiohttp import web
from django.conf import settings

from pulpcore.plugin.content import app
from pulp_container.app.registry import Registry

registry = Registry()

PREFIX = "/pulp/container/{pulp_domain}/" if settings.DOMAIN_ENABLED else "/pulp/container/"

app.add_routes(
[
web.get(
r"/pulp/container/{path:.+}/{content:(blobs|manifests)}/sha256:{digest:.+}",
PREFIX + r"{path:.+}/{content:(blobs|manifests)}/sha256:{digest:.+}",
registry.get_by_digest,
)
]
)
app.add_routes([web.get(r"/pulp/container/{path:.+}/manifests/{tag_name}", registry.get_tag)])
app.add_routes([web.get(PREFIX + r"{path:.+}/manifests/{tag_name}", registry.get_tag)])
28 changes: 21 additions & 7 deletions pulp_container/app/global_access_conditions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from logging import getLogger
from django.conf import settings

from pulpcore.plugin.models import Repository
from pulpcore.plugin.viewsets import RepositoryVersionViewSet
Expand All @@ -12,10 +13,13 @@
def has_namespace_obj_perms(request, view, action, permission):
"""
Check if a user has object-level perms on the namespace associated with the distribution
or repository.
or repository. If they have model/domain level permission then return True.
"""
if request.user.has_perm(permission):
return True
if settings.DOMAIN_ENABLED:
if request.user.has_perm(permission, request.pulp_domain):
return True
if isinstance(view, RepositoryVersionViewSet):
obj = Repository.objects.get(pk=view.kwargs["repository_pk"]).cast()
else:
Expand Down Expand Up @@ -44,23 +48,31 @@ def has_namespace_perms(request, view, action, permission):
return False
namespace = base_path.split("/")[0]
try:
namespace = models.ContainerNamespace.objects.get(name=namespace)
namespace = models.ContainerNamespace.objects.get(
name=namespace, pulp_domain=request.pulp_domain
)
except models.ContainerNamespace.DoesNotExist:
return False
else:
return request.user.has_perm(permission) or request.user.has_perm(ns_perm, namespace)
return (
request.user.has_perm(permission)
or request.user.has_perm(permission, request.pulp_domain)
or request.user.has_perm(ns_perm, namespace)
)


def has_namespace_or_obj_perms(request, view, action, permission):
"""
Check if a user has a namespace-level perms or object-level permission
Check if a user has a namespace-level perms or permissions on the original object
"""
ns_perm = "container.namespace_{}".format(permission.split(".", 1)[1])
if has_namespace_obj_perms(request, view, action, ns_perm):
return True
else:
return request.user.has_perm(permission) or request.user.has_perm(
permission, view.get_object()
return (
request.user.has_perm(permission)
or request.user.has_perm(permission, request.pulp_domain)
or request.user.has_perm(permission, view.get_object())
)


Expand Down Expand Up @@ -99,13 +111,15 @@ def has_namespace_model_perms(request, view, action):
"""
if request.user.has_perm("container.add_containernamespace"):
return True
if settings.DOMAIN_ENABLED:
return request.user.has_perm("container.add_containernamespace", obj=request.pulp_domain)
return False


def has_distribution_perms(request, view, action, permission):
"""
Check if the user has permissions on the corresponding distribution.
Model or object permission is sufficient.
Model, domain or object permission is sufficient.
"""
if request.user.has_perm(permission):
return True
Expand Down
81 changes: 81 additions & 0 deletions pulp_container/app/migrations/0044_add_domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Generated by Django 4.2.16 on 2024-11-21 20:59

from django.db import migrations, models
import django.db.models.deletion
import pulpcore.app.util


class Migration(migrations.Migration):

dependencies = [
('core', '0125_openpgpdistribution_openpgpkeyring_openpgppublickey_and_more'),
('container', '0043_add_os_arch_image_size_manifest_fields'),
]

operations = [
migrations.AlterUniqueTogether(
name='blob',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='containernamespace',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='manifest',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='manifestsignature',
unique_together=set(),
),
migrations.AlterUniqueTogether(
name='tag',
unique_together=set(),
),
migrations.AddField(
model_name='blob',
name='_pulp_domain',
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
),
migrations.AddField(
model_name='containernamespace',
name='pulp_domain',
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
),
migrations.AddField(
model_name='manifest',
name='_pulp_domain',
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
),
migrations.AddField(
model_name='manifestsignature',
name='_pulp_domain',
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
),
migrations.AddField(
model_name='tag',
name='_pulp_domain',
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
),
migrations.AlterUniqueTogether(
name='blob',
unique_together={('digest', '_pulp_domain')},
),
migrations.AlterUniqueTogether(
name='containernamespace',
unique_together={('name', 'pulp_domain')},
),
migrations.AlterUniqueTogether(
name='manifest',
unique_together={('digest', '_pulp_domain')},
),
migrations.AlterUniqueTogether(
name='manifestsignature',
unique_together={('digest', '_pulp_domain')},
),
migrations.AlterUniqueTogether(
name='tag',
unique_together={('name', 'tagged_manifest', '_pulp_domain')},
),
]
Loading

0 comments on commit 29b5466

Please sign in to comment.