Skip to content

Commit 26cf951

Browse files
committed
Add initial support for domains
1 parent a38a102 commit 26cf951

17 files changed

+325
-150
lines changed

CHANGES/domain-enablement.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add partial support for Domains. The plugin can be installed with the feature turned on, but it
2+
only functions within the default domain.

pulp_container/app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class PulpContainerPluginAppConfig(PulpPluginAppConfig):
88
label = "container"
99
version = "2.23.0.dev"
1010
python_package_name = "pulp-container"
11+
domain_compatible = True
1112

1213
def ready(self):
1314
super().ready()

pulp_container/app/authorization.py

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from cryptography.hazmat.backends import default_backend
1818
from cryptography.hazmat.primitives import serialization
1919

20+
from pulpcore.plugin.util import get_domain
2021
from pulp_container.app.models import (
2122
ContainerDistribution,
2223
ContainerNamespace,
@@ -209,10 +210,10 @@ def generate_claim_set(issuer, issued_at, subject, audience, access):
209210
}
210211

211212

212-
def get_pull_through_distribution(path):
213+
def get_pull_through_distribution(path, domain):
213214
return (
214215
ContainerPullThroughDistribution.objects.annotate(path=Value(path))
215-
.filter(path__startswith=F("base_path"))
216+
.filter(pulp_domain=domain, path__startswith=F("base_path"))
216217
.order_by("-base_path")
217218
.first()
218219
)
@@ -239,50 +240,65 @@ def has_pull_permissions(self, path):
239240
"""
240241
Check if the user has permissions to pull from the repository specified by the path.
241242
"""
243+
domain = get_domain()
242244
try:
243-
distribution = ContainerDistribution.objects.get(base_path=path)
245+
distribution = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
244246
except ContainerDistribution.DoesNotExist:
245247
namespace_name = path.split("/")[0]
246248
try:
247-
namespace = ContainerNamespace.objects.get(name=namespace_name)
249+
namespace = ContainerNamespace.objects.get(name=namespace_name, pulp_domain=domain)
248250
except ContainerNamespace.DoesNotExist:
249251
# Check if the user is allowed to create a new namespace
250-
return self.has_permission(None, "POST", "create", {"name": namespace_name})
252+
return self.has_permission(
253+
None, "POST", "create", {"name": namespace_name, "pulp_domain": domain}
254+
)
251255

252256
if pt_distribution := get_pull_through_distribution(path):
253257
# Check if the user is allowed to create a new distribution
254258
return self.has_pull_through_new_distribution_permissions(pt_distribution)
255259
else:
256260
# Check if the user is allowed to view distributions in the namespace
257261
return self.has_permission(
258-
namespace, "GET", "view_distribution", {"name": namespace_name}
262+
namespace,
263+
"GET",
264+
"view_distribution",
265+
{"name": namespace_name, "pulp_domain": domain},
259266
)
260267

261-
if pt_distribution := get_pull_through_distribution(path):
268+
if pt_distribution := get_pull_through_distribution(path, domain):
262269
# Check if the user is allowed to pull new content via a pull-through distribution
263-
if self.has_pull_through_permissions(distribution):
270+
if self.has_pull_through_permissions(
271+
pt_distribution
272+
): # was this using the wrong variable?
264273
return True
265274

266275
# Check if the user has general pull permissions
267-
return self.has_permission(distribution, "GET", "pull", {"base_path": path})
276+
return self.has_permission(
277+
distribution, "GET", "pull", {"base_path": path, "pulp_domain": domain}
278+
)
268279

269280
def has_push_permissions(self, path):
270281
"""
271282
Check if the user has permissions to push to the repository specified by the path.
272283
"""
284+
domain = get_domain()
273285
try:
274-
distribution = ContainerDistribution.objects.get(base_path=path)
286+
distribution = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
275287
except ContainerDistribution.DoesNotExist:
276288
namespace_name = path.split("/")[0]
277289
try:
278-
namespace = ContainerNamespace.objects.get(name=namespace_name)
290+
namespace = ContainerNamespace.objects.get(name=namespace_name, pulp_domain=domain)
279291
except ContainerNamespace.DoesNotExist:
280292
# Check if user is allowed to create a new namespace
281-
return self.has_permission(None, "POST", "create", {"name": namespace_name})
293+
return self.has_permission(
294+
None, "POST", "create", {"name": namespace_name, "pulp_domain": domain}
295+
)
282296
# Check if user is allowed to create a new distribution in the namespace
283297
return self.has_permission(namespace, "POST", "create_distribution", {})
284298

285-
return self.has_permission(distribution, "POST", "push", {"base_path": path})
299+
return self.has_permission(
300+
distribution, "POST", "push", {"base_path": path, "pulp_domain": domain}
301+
)
286302

287303
def has_view_catalog_permissions(self, path):
288304
"""

pulp_container/app/cache.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.db.models import F, Value
33

44
from pulpcore.plugin.cache import CacheKeys, AsyncContentCache, SyncContentCache
5+
from pulpcore.plugin.util import get_domain, cache_key
56

67
from pulp_container.app.models import ContainerDistribution, ContainerPullThroughDistribution
78
from pulp_container.app.exceptions import RepositoryNotFound
@@ -65,16 +66,17 @@ def find_base_path_cached(request, cached):
6566
6667
"""
6768
path = request.resolver_match.kwargs["path"]
68-
path_exists = cached.exists(base_key=path)
69+
path_exists = cached.exists(base_key=cache_key(path))
6970
if path_exists:
7071
return path
7172
else:
73+
domain = get_domain()
7274
try:
73-
distro = ContainerDistribution.objects.get(base_path=path)
75+
distro = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
7476
except ObjectDoesNotExist:
7577
distro = (
7678
ContainerPullThroughDistribution.objects.annotate(path=Value(path))
77-
.filter(path__startswith=F("base_path"))
79+
.filter(path__startswith=F("base_path"), pulp_domain=domain)
7880
.order_by("-base_path")
7981
.first()
8082
)

pulp_container/app/content.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
from aiohttp import web
2+
from django.conf import settings
23

34
from pulpcore.plugin.content import app
45
from pulp_container.app.registry import Registry
56

67
registry = Registry()
78

9+
PREFIX = "/pulp/container/{pulp_domain}/" if settings.DOMAIN_ENABLED else "/pulp/container/"
10+
811
app.add_routes(
912
[
1013
web.get(
11-
r"/pulp/container/{path:.+}/{content:(blobs|manifests)}/sha256:{digest:.+}",
14+
PREFIX + r"{path:.+}/{content:(blobs|manifests)}/sha256:{digest:.+}",
1215
registry.get_by_digest,
1316
)
1417
]
1518
)
16-
app.add_routes([web.get(r"/pulp/container/{path:.+}/manifests/{tag_name}", registry.get_tag)])
19+
app.add_routes([web.get(PREFIX + r"{path:.+}/manifests/{tag_name}", registry.get_tag)])

pulp_container/app/global_access_conditions.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from logging import getLogger
2+
from django.conf import settings
23

34
from pulpcore.plugin.models import Repository
45
from pulpcore.plugin.viewsets import RepositoryVersionViewSet
@@ -12,10 +13,13 @@
1213
def has_namespace_obj_perms(request, view, action, permission):
1314
"""
1415
Check if a user has object-level perms on the namespace associated with the distribution
15-
or repository.
16+
or repository. If they have model/domain level permission then return True.
1617
"""
1718
if request.user.has_perm(permission):
1819
return True
20+
if settings.DOMAIN_ENABLED:
21+
if request.user.has_perm(permission, request.pulp_domain):
22+
return True
1923
if isinstance(view, RepositoryVersionViewSet):
2024
obj = Repository.objects.get(pk=view.kwargs["repository_pk"]).cast()
2125
else:
@@ -42,26 +46,18 @@ def has_namespace_perms(request, view, action, permission):
4246
base_path = request.data.get("base_path")
4347
if not base_path:
4448
return False
45-
namespace = base_path.split("/")[0]
46-
try:
47-
namespace = models.ContainerNamespace.objects.get(name=namespace)
48-
except models.ContainerNamespace.DoesNotExist:
49-
return False
50-
else:
51-
return request.user.has_perm(permission) or request.user.has_perm(ns_perm, namespace)
49+
return has_namespace_obj_perms(request, view, action, ns_perm)
5250

5351

5452
def has_namespace_or_obj_perms(request, view, action, permission):
5553
"""
56-
Check if a user has a namespace-level perms or object-level permission
54+
Check if a user has a namespace-level perms or permissions on the original object
5755
"""
5856
ns_perm = "container.namespace_{}".format(permission.split(".", 1)[1])
5957
if has_namespace_obj_perms(request, view, action, ns_perm):
6058
return True
6159
else:
62-
return request.user.has_perm(permission) or request.user.has_perm(
63-
permission, view.get_object()
64-
)
60+
return request.user.has_perm(permission, view.get_object())
6561

6662

6763
def obj_exists(request, view, action):
@@ -99,13 +95,15 @@ def has_namespace_model_perms(request, view, action):
9995
"""
10096
if request.user.has_perm("container.add_containernamespace"):
10197
return True
98+
if settings.DOMAIN_ENABLED:
99+
return request.user.has_perm("container.add_containernamespace", obj=request.pulp_domain)
102100
return False
103101

104102

105103
def has_distribution_perms(request, view, action, permission):
106104
"""
107105
Check if the user has permissions on the corresponding distribution.
108-
Model or object permission is sufficient.
106+
Model, domain or object permission is sufficient.
109107
"""
110108
if request.user.has_perm(permission):
111109
return True
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Generated by Django 4.2.16 on 2024-11-21 20:59
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import pulpcore.app.util
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('core', '0125_openpgpdistribution_openpgpkeyring_openpgppublickey_and_more'),
12+
('container', '0043_add_os_arch_image_size_manifest_fields'),
13+
]
14+
15+
operations = [
16+
migrations.AlterUniqueTogether(
17+
name='blob',
18+
unique_together=set(),
19+
),
20+
migrations.AlterUniqueTogether(
21+
name='containernamespace',
22+
unique_together=set(),
23+
),
24+
migrations.AlterUniqueTogether(
25+
name='manifest',
26+
unique_together=set(),
27+
),
28+
migrations.AlterUniqueTogether(
29+
name='manifestsignature',
30+
unique_together=set(),
31+
),
32+
migrations.AlterUniqueTogether(
33+
name='tag',
34+
unique_together=set(),
35+
),
36+
migrations.AddField(
37+
model_name='blob',
38+
name='_pulp_domain',
39+
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
40+
),
41+
migrations.AddField(
42+
model_name='containernamespace',
43+
name='pulp_domain',
44+
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
45+
),
46+
migrations.AddField(
47+
model_name='manifest',
48+
name='_pulp_domain',
49+
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
50+
),
51+
migrations.AddField(
52+
model_name='manifestsignature',
53+
name='_pulp_domain',
54+
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
55+
),
56+
migrations.AddField(
57+
model_name='tag',
58+
name='_pulp_domain',
59+
field=models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain'),
60+
),
61+
migrations.AlterUniqueTogether(
62+
name='blob',
63+
unique_together={('digest', '_pulp_domain')},
64+
),
65+
migrations.AlterUniqueTogether(
66+
name='containernamespace',
67+
unique_together={('name', 'pulp_domain')},
68+
),
69+
migrations.AlterUniqueTogether(
70+
name='manifest',
71+
unique_together={('digest', '_pulp_domain')},
72+
),
73+
migrations.AlterUniqueTogether(
74+
name='manifestsignature',
75+
unique_together={('digest', '_pulp_domain')},
76+
),
77+
migrations.AlterUniqueTogether(
78+
name='tag',
79+
unique_together={('name', 'tagged_manifest', '_pulp_domain')},
80+
),
81+
]

pulp_container/app/models.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
Upload as CoreUpload,
2828
)
2929
from pulpcore.plugin.repo_version_utils import remove_duplicates, validate_repo_version
30-
from pulpcore.plugin.util import gpg_verify
30+
from pulpcore.plugin.util import gpg_verify, get_domain_pk
3131

3232

3333
from . import downloaders
@@ -63,10 +63,11 @@ class Blob(Content):
6363
TYPE = "blob"
6464

6565
digest = models.TextField(db_index=True)
66+
_pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT)
6667

6768
class Meta:
6869
default_related_name = "%(app_label)s_%(model_name)s"
69-
unique_together = ("digest",)
70+
unique_together = ("digest", "_pulp_domain")
7071

7172

7273
class Manifest(Content):
@@ -138,6 +139,7 @@ class Manifest(Content):
138139
symmetrical=False,
139140
through_fields=("image_manifest", "manifest_list"),
140141
)
142+
_pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT)
141143

142144
def __init__(self, *args, **kwargs):
143145
self._json_manifest = None
@@ -302,7 +304,7 @@ def is_artifact(self):
302304

303305
class Meta:
304306
default_related_name = "%(app_label)s_%(model_name)s"
305-
unique_together = ("digest",)
307+
unique_together = ("digest", "_pulp_domain")
306308

307309

308310
class BlobManifest(models.Model):
@@ -381,10 +383,11 @@ class Tag(Content):
381383
tagged_manifest = models.ForeignKey(
382384
Manifest, null=False, related_name="tagged_manifests", on_delete=models.CASCADE
383385
)
386+
_pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT)
384387

385388
class Meta:
386389
default_related_name = "%(app_label)s_%(model_name)s"
387-
unique_together = (("name", "tagged_manifest"),)
390+
unique_together = ("name", "tagged_manifest", "_pulp_domain")
388391

389392

390393
class ManifestSignature(Content):
@@ -421,12 +424,13 @@ class ManifestSignature(Content):
421424
signed_manifest = models.ForeignKey(
422425
Manifest, null=False, related_name="signed_manifests", on_delete=models.CASCADE
423426
)
427+
_pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT)
424428
# TODO: Maybe there should be an optional field with a FK to a signing_service for the cases
425429
# when Pulp creates a signature.
426430

427431
class Meta:
428432
default_related_name = "%(app_label)s_%(model_name)s"
429-
unique_together = (("digest",),)
433+
unique_together = ("digest", "_pulp_domain")
430434

431435

432436
class ContainerNamespace(BaseModel, AutoAddObjPermsMixin):
@@ -438,9 +442,10 @@ class ContainerNamespace(BaseModel, AutoAddObjPermsMixin):
438442
"""
439443

440444
name = models.TextField(db_index=True)
445+
pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT)
441446

442447
class Meta:
443-
unique_together = (("name",),)
448+
unique_together = ("name", "pulp_domain")
444449
permissions = [
445450
("namespace_add_containerdistribution", "Add any distribution to a namespace"),
446451
("namespace_delete_containerdistribution", "Delete any distribution from a namespace"),

0 commit comments

Comments
 (0)