Skip to content

Commit 937b7de

Browse files
Apply review feedback
1 parent f783cc6 commit 937b7de

7 files changed

Lines changed: 73 additions & 22 deletions

File tree

docs/admin/guides/auth/workload_identity.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,25 @@ A token that matches no rule is rejected with a 401.
2626

2727
## Enabling
2828

29-
Add the authentication class to `DEFAULT_AUTHENTICATION_CLASSES`,
30-
then populate `WORKLOAD_IDENTITY`:
29+
Add the authentication class to `DEFAULT_AUTHENTICATION_CLASSES`, add the backend to
30+
`AUTHENTICATION_BACKENDS`, then populate `WORKLOAD_IDENTITY`:
3131

3232
```python title="settings.py"
3333
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"] = [
3434
"pulpcore.app.workload_identity.authentication.WorkloadIdentityAuthentication",
3535
"pulpcore.app.authentication.BasicAuthentication",
3636
"rest_framework.authentication.SessionAuthentication",
3737
]
38+
39+
AUTHENTICATION_BACKENDS = [
40+
"django.contrib.auth.backends.ModelBackend",
41+
"pulpcore.backends.ObjectRolePermissionBackend",
42+
"pulpcore.app.workload_identity.backend.WorkloadIdentityBackend",
43+
]
3844
```
3945

40-
No change to `AUTHENTICATION_BACKENDS` is needed.
41-
The feature stays off while `WORKLOAD_IDENTITY` is empty,
42-
so adding the class alone changes nothing.
46+
The backend answers the permission checks for a workload identity request, so it must be present
47+
for the feature to grant anything. Nothing is active until all three pieces are configured.
4348

4449
With the example below,
4550
a push from the `main` branch of `my-org/app` is granted the `file.filerepository_owner` role on the repository named `prod`,

pulpcore/app/access_policy.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from copy import deepcopy
22

33
from django.conf import settings
4+
from django.contrib.auth import get_user_model
45
from rest_access_policy import AccessPolicy
56
from rest_framework.exceptions import APIException
67

@@ -22,8 +23,6 @@ def get_user_group_values(self, user):
2223
which does not work for a stateless principal. Any non-database user (a workload-identity
2324
principal, or another one added later) supplies its groups via a `group_names` attribute.
2425
"""
25-
from django.contrib.auth import get_user_model
26-
2726
if isinstance(user, get_user_model()):
2827
return super().get_user_group_values(user)
2928
return list(getattr(user, "group_names", []))

pulpcore/app/checks.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,14 @@ def check_artifact_checksums(app_configs, **kwargs):
125125
return messages
126126

127127

128+
_WORKLOAD_IDENTITY_BACKEND = "pulpcore.app.workload_identity.backend.WorkloadIdentityBackend"
129+
130+
128131
@register(deploy=True)
129132
def workload_identity_reserved_username(app_configs, **kwargs):
133+
if _WORKLOAD_IDENTITY_BACKEND not in settings.AUTHENTICATION_BACKENDS:
134+
return []
135+
130136
from pulpcore.app.workload_identity import config
131137

132138
messages = []
@@ -157,6 +163,9 @@ def workload_identity_reserved_username(app_configs, **kwargs):
157163

158164
@register(deploy=True)
159165
def workload_identity_domain_scopes(app_configs, **kwargs):
166+
if _WORKLOAD_IDENTITY_BACKEND not in settings.AUTHENTICATION_BACKENDS:
167+
return []
168+
160169
from pulpcore.app.workload_identity import config
161170

162171
messages = []
@@ -183,6 +192,9 @@ def workload_identity_domain_scopes(app_configs, **kwargs):
183192

184193
@register(deploy=True)
185194
def workload_identity_unqualified_name_scopes(app_configs, **kwargs):
195+
if _WORKLOAD_IDENTITY_BACKEND not in settings.AUTHENTICATION_BACKENDS:
196+
return []
197+
186198
from pulpcore.app.workload_identity import config
187199

188200
messages = []

pulpcore/app/role_util.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -173,23 +173,27 @@ def get_objects_for_user(
173173
accept_domain_perms=True,
174174
accept_global_perms=True,
175175
):
176-
from pulpcore.app.workload_identity.principal import WorkloadIdentityPrincipal
176+
if (
177+
"pulpcore.app.workload_identity.backend.WorkloadIdentityBackend"
178+
in settings.AUTHENTICATION_BACKENDS
179+
):
180+
from pulpcore.app.workload_identity.principal import WorkloadIdentityPrincipal
177181

178-
if isinstance(user, WorkloadIdentityPrincipal):
179-
from pulpcore.app.workload_identity.authz import grants_queryset
182+
if isinstance(user, WorkloadIdentityPrincipal):
183+
from pulpcore.app.workload_identity.authz import grants_queryset
180184

181-
grants = user.grants
182-
if isinstance(perms, str):
183-
return grants_queryset(grants, perms, qs)
184-
if any_perm:
185-
result = qs.none()
185+
grants = user.grants
186+
if isinstance(perms, str):
187+
return grants_queryset(grants, perms, qs)
188+
if any_perm:
189+
result = qs.none()
190+
for permission_name in perms:
191+
result |= grants_queryset(grants, permission_name, qs)
192+
return result
193+
result = qs.all()
186194
for permission_name in perms:
187-
result |= grants_queryset(grants, permission_name, qs)
195+
result &= grants_queryset(grants, permission_name, qs)
188196
return result
189-
result = qs.all()
190-
for permission_name in perms:
191-
result &= grants_queryset(grants, permission_name, qs)
192-
return result
193197

194198
new_qs = qs.none()
195199
replace = False

pulpcore/app/settings.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@
155155
AUTHENTICATION_BACKENDS = [
156156
"django.contrib.auth.backends.ModelBackend",
157157
"pulpcore.backends.ObjectRolePermissionBackend",
158-
"pulpcore.app.workload_identity.backend.WorkloadIdentityBackend",
159158
]
160159

161160
ROOT_URLCONF = "pulpcore.app.urls"

pulpcore/tests/unit/workload_identity/test_workload_identity.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@
3232
from pulpcore.app.workload_identity.principal import WorkloadIdentityPrincipal
3333
from pulpcore.app.workload_identity.rules import grants_for
3434

35+
WI_BACKENDS = [
36+
"django.contrib.auth.backends.ModelBackend",
37+
"pulpcore.backends.ObjectRolePermissionBackend",
38+
"pulpcore.app.workload_identity.backend.WorkloadIdentityBackend",
39+
]
40+
41+
42+
@pytest.fixture(autouse=True)
43+
def _wi_backend_enabled(settings):
44+
# The backend is an opt-in addon, so enable it for the tests that exercise it.
45+
settings.AUTHENTICATION_BACKENDS = WI_BACKENDS
46+
47+
3548
PROVIDER = {
3649
"issuer": "https://issuer",
3750
"rules": [
@@ -622,3 +635,22 @@ def test_authenticate_not_a_jwt_returns_none():
622635
@override_settings(WORKLOAD_IDENTITY=WI_AUTH)
623636
def test_authenticate_no_token_returns_none():
624637
assert WorkloadIdentityAuthentication().authenticate(_FakeRequest("")) is None
638+
639+
640+
# --- checks are silent when the backend is not configured ---
641+
642+
643+
@override_settings(
644+
AUTHENTICATION_BACKENDS=["django.contrib.auth.backends.ModelBackend"],
645+
DOMAIN_ENABLED=True,
646+
WORKLOAD_IDENTITY={
647+
"basic_auth_username": "ci-bot",
648+
"providers": {
649+
"p": {"rules": [{"grants": [{"role": "r", "scope": {"type": "object", "name": "x"}}]}]}
650+
},
651+
},
652+
)
653+
def test_checks_silent_when_backend_not_configured():
654+
assert workload_identity_reserved_username(None) == []
655+
assert workload_identity_domain_scopes(None) == []
656+
assert workload_identity_unqualified_name_scopes(None) == []

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ dependencies = [
3131
"backoff>=2.1.2,<2.3", # Looks like only bugfixes in z-Stream.
3232
"click>=8.1.0,<8.5", # Uses milestone.feature.fix https://palletsprojects.com/versions .
3333
"cryptography>=49.0.0,<51.0", # SemVer compatible https://cryptography.io/en/latest/api-stability/#versioning .
34-
"pyjwt>=2.4,<3", # >=2.4 fixes CVE-2022-29217, capped below the next major. https://pyjwt.readthedocs.io/en/stable/changelog.html
34+
"pyjwt>=2.4,<3", # SemVer compatible https://pyjwt.readthedocs.io/en/stable/changelog.html .
3535
"Django>=5.2.0,<5.3", # LTS version, we aim at supporting one or two at a time.
3636
"django-filter>=24.3,<=26.1", # Uses CalVer, not released often https://github.com/carltongibson/django-filter
3737
"django-guid>=3.4.0,<3.7", # Looks like only bugfixes in z-Stream.

0 commit comments

Comments
 (0)