Skip to content

Commit 17832c6

Browse files
committed
Add initial support for domains
1 parent 88b2002 commit 17832c6

25 files changed

+365
-194
lines changed

Diff for: .ci/ansible/settings.py.j2

+2-23
Original file line numberDiff line numberDiff line change
@@ -27,39 +27,18 @@ API_ROOT = {{ api_root | repr }}
2727
{% endif %}
2828

2929
{% if s3_test | default(false) %}
30-
MEDIA_ROOT: ""
31-
S3_USE_SIGV4 = True
32-
{% if test_storages_compat_layer is defined and test_storages_compat_layer %}
33-
STORAGES = {
34-
"default": {
35-
"BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
36-
"OPTIONS": {
37-
"access_key": "{{ minio_access_key }}",
38-
"secret_key": "{{ minio_secret_key }}",
39-
"region_name": "eu-central-1",
40-
"addressing_style": "path",
41-
"signature_version": "s3v4",
42-
"bucket_name": "pulp3",
43-
"endpoint_url": "http://minio:9000",
44-
"default_acl": "@none None",
45-
},
46-
},
47-
"staticfiles": {
48-
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
49-
},
50-
}
51-
{% else %}
5230
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
31+
MEDIA_ROOT = ""
5332
AWS_ACCESS_KEY_ID = "{{ minio_access_key }}"
5433
AWS_SECRET_ACCESS_KEY = "{{ minio_secret_key }}"
5534
AWS_S3_REGION_NAME = "eu-central-1"
5635
AWS_S3_ADDRESSING_STYLE = "path"
36+
S3_USE_SIGV4 = True
5737
AWS_S3_SIGNATURE_VERSION = "s3v4"
5838
AWS_STORAGE_BUCKET_NAME = "pulp3"
5939
AWS_S3_ENDPOINT_URL = "http://minio:9000"
6040
AWS_DEFAULT_ACL = "@none None"
6141
{% endif %}
62-
{% endif %}
6342

6443
{% if azure_test | default(false) %}
6544
DEFAULT_FILE_STORAGE = "storages.backends.azure_storage.AzureStorage"

Diff for: .github/template_gitref

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2021.08.26-420-gf332a34
1+
2021.08.26-417-gcec8e48

Diff for: .github/workflows/scripts/install.sh

+1-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ minio_access_key: "'$MINIO_ACCESS_KEY'"\
9797
minio_secret_key: "'$MINIO_SECRET_KEY'"\
9898
pulp_scenario_settings: {"flatpak_index": false, "token_auth_disabled": true}\
9999
pulp_scenario_env: {}\
100-
test_storages_compat_layer: false\
101100
' vars/main.yaml
102101
export PULP_API_ROOT="/rerouted/djnd/"
103102
fi
@@ -110,7 +109,7 @@ if [ "$TEST" = "azure" ]; then
110109
- ./azurite:/etc/pulp\
111110
command: "azurite-blob --blobHost 0.0.0.0"' vars/main.yaml
112111
sed -i -e '$a azure_test: true\
113-
pulp_scenario_settings: {"flatpak_index": true}\
112+
pulp_scenario_settings: {"domain_enabled": true, "flatpak_index": true}\
114113
pulp_scenario_env: {}\
115114
' vars/main.yaml
116115
fi

Diff for: CHANGES/domain-enablement.feature

+2
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.

Diff for: pulp_container/app/__init__.py

+1
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()

Diff for: pulp_container/app/authorization.py

+28-13
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
)
@@ -231,6 +232,7 @@ def has_permission(self, obj, method, action, data):
231232
request.method = method
232233
request.user = self.user
233234
request._full_data = data
235+
request.pulp_domain = get_domain()
234236
# Fake the corresponding view
235237
view = FakeViewWithSerializer(action, lambda: obj)
236238
return self.access_policy.has_permission(request, view)
@@ -239,50 +241,63 @@ def has_pull_permissions(self, path):
239241
"""
240242
Check if the user has permissions to pull from the repository specified by the path.
241243
"""
244+
domain = get_domain()
242245
try:
243-
distribution = ContainerDistribution.objects.get(base_path=path)
246+
distribution = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
244247
except ContainerDistribution.DoesNotExist:
245248
namespace_name = path.split("/")[0]
246249
try:
247-
namespace = ContainerNamespace.objects.get(name=namespace_name)
250+
namespace = ContainerNamespace.objects.get(name=namespace_name, pulp_domain=domain)
248251
except ContainerNamespace.DoesNotExist:
249252
# Check if the user is allowed to create a new namespace
250-
return self.has_permission(None, "POST", "create", {"name": namespace_name})
253+
return self.has_permission(
254+
None, "POST", "create", {"name": namespace_name, "pulp_domain": domain}
255+
)
251256

252-
if pt_distribution := get_pull_through_distribution(path):
257+
if pt_distribution := get_pull_through_distribution(path, domain):
253258
# Check if the user is allowed to create a new distribution
254259
return self.has_pull_through_new_distribution_permissions(pt_distribution)
255260
else:
256261
# Check if the user is allowed to view distributions in the namespace
257262
return self.has_permission(
258-
namespace, "GET", "view_distribution", {"name": namespace_name}
263+
namespace,
264+
"GET",
265+
"view_distribution",
266+
{"name": namespace_name, "pulp_domain": domain},
259267
)
260268

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

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

269279
def has_push_permissions(self, path):
270280
"""
271281
Check if the user has permissions to push to the repository specified by the path.
272282
"""
283+
domain = get_domain()
273284
try:
274-
distribution = ContainerDistribution.objects.get(base_path=path)
285+
distribution = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
275286
except ContainerDistribution.DoesNotExist:
276287
namespace_name = path.split("/")[0]
277288
try:
278-
namespace = ContainerNamespace.objects.get(name=namespace_name)
289+
namespace = ContainerNamespace.objects.get(name=namespace_name, pulp_domain=domain)
279290
except ContainerNamespace.DoesNotExist:
280291
# Check if user is allowed to create a new namespace
281-
return self.has_permission(None, "POST", "create", {"name": namespace_name})
292+
return self.has_permission(
293+
None, "POST", "create", {"name": namespace_name, "pulp_domain": domain}
294+
)
282295
# Check if user is allowed to create a new distribution in the namespace
283296
return self.has_permission(namespace, "POST", "create_distribution", {})
284297

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

287302
def has_view_catalog_permissions(self, path):
288303
"""

Diff for: pulp_container/app/cache.py

+5-3
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
)

Diff for: pulp_container/app/content.py

+5-2
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)])

Diff for: pulp_container/app/global_access_conditions.py

+21-7
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:
@@ -44,23 +48,31 @@ def has_namespace_perms(request, view, action, permission):
4448
return False
4549
namespace = base_path.split("/")[0]
4650
try:
47-
namespace = models.ContainerNamespace.objects.get(name=namespace)
51+
namespace = models.ContainerNamespace.objects.get(
52+
name=namespace, pulp_domain=request.pulp_domain
53+
)
4854
except models.ContainerNamespace.DoesNotExist:
4955
return False
5056
else:
51-
return request.user.has_perm(permission) or request.user.has_perm(ns_perm, namespace)
57+
return (
58+
request.user.has_perm(permission)
59+
or request.user.has_perm(permission, request.pulp_domain)
60+
or request.user.has_perm(ns_perm, namespace)
61+
)
5262

5363

5464
def has_namespace_or_obj_perms(request, view, action, permission):
5565
"""
56-
Check if a user has a namespace-level perms or object-level permission
66+
Check if a user has a namespace-level perms or permissions on the original object
5767
"""
5868
ns_perm = "container.namespace_{}".format(permission.split(".", 1)[1])
5969
if has_namespace_obj_perms(request, view, action, ns_perm):
6070
return True
6171
else:
62-
return request.user.has_perm(permission) or request.user.has_perm(
63-
permission, view.get_object()
72+
return (
73+
request.user.has_perm(permission)
74+
or request.user.has_perm(permission, request.pulp_domain)
75+
or request.user.has_perm(permission, view.get_object())
6476
)
6577

6678

@@ -99,13 +111,15 @@ def has_namespace_model_perms(request, view, action):
99111
"""
100112
if request.user.has_perm("container.add_containernamespace"):
101113
return True
114+
if settings.DOMAIN_ENABLED:
115+
return request.user.has_perm("container.add_containernamespace", obj=request.pulp_domain)
102116
return False
103117

104118

105119
def has_distribution_perms(request, view, action, permission):
106120
"""
107121
Check if the user has permissions on the corresponding distribution.
108-
Model or object permission is sufficient.
122+
Model, domain or object permission is sufficient.
109123
"""
110124
if request.user.has_perm(permission):
111125
return True

Diff for: pulp_container/app/migrations/0044_add_domain.py

+81
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+
]

0 commit comments

Comments
 (0)