From c7c7f494ad7612cc8c56cc5c4bd1aa646b1d872f Mon Sep 17 00:00:00 2001 From: Bryan ramos Date: Wed, 30 Sep 2026 16:23:16 -0400 Subject: [PATCH] test: send a real upload body in the lightwell POST-denial test The lightwell POST-denial test posted an empty JSON body to a content-create endpoint that only accepts multipart uploads. That 400s during request parsing, before RBAC ever runs, so to keep the test green the assertion had to tolerate a 400 alongside 401/403, which muddied what it was actually proving: that a subscribed-but-unroled caller can't POST content (the subscription grant only covers safe reads). Instead of widening the assertion, send a complete, valid upload body: a file, a relative_path, and the repository the fixture's owner created. The request now clears multipart parsing and pulpcore's create conditions (which validate the serializer before deciding authorization) and is denied by the access policy itself: has_required_repo_perms_on_upload:file.modify_filerepository returns a clean 403 because the caller holds no role on that repo. The assertion is a strict 403, so the test fails if the caller is instead rejected during authentication rather than by the RBAC policy. Verified in the dev container: the test passes and the denial is specifically a 403. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Bryan ramos --- ...st_lightwell_content_listing_permission.py | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/pulp_service/pulp_service/tests/functional/test_lightwell_content_listing_permission.py b/pulp_service/pulp_service/tests/functional/test_lightwell_content_listing_permission.py index 30ebf80c..076d52c3 100644 --- a/pulp_service/pulp_service/tests/functional/test_lightwell_content_listing_permission.py +++ b/pulp_service/pulp_service/tests/functional/test_lightwell_content_listing_permission.py @@ -148,20 +148,32 @@ def test_domain_owner_can_list_content(configure_lightwell_domain): def test_entitled_org_post_denied_on_content_endpoint(configure_lightwell_domain): """A subscribed user cannot POST to a content listing endpoint -- the subscription check only applies to SAFE_METHODS (GET/HEAD/OPTIONS), so the POST falls through to the typed - content-create policy. - - That policy never yields a clean 403 for this request: pulpcore's create conditions - (has_required_repo_perms_on_upload / has_upload_param_model_or_domain_or_obj_perms) call - serializer.is_valid(raise_exception=True) before deciding authorization, so the empty body - 400s during permission evaluation. The assertion is therefore "POST is rejected, not - granted" -- a subscription grant would only ever turn a SAFE read into a 200, never a POST - into a 2xx.""" - content_url, _, _ = configure_lightwell_domain + content-create policy and is denied. + + Send a complete, valid file-upload body (file + relative_path + repository) so the request + clears multipart parsing and pulpcore's create conditions -- which call + serializer.is_valid(raise_exception=True) before deciding authorization -- reach the actual + permission check. The entitled caller holds no role on the target repository, so + has_required_repo_perms_on_upload:file.modify_filerepository denies with a clean 403 rather + than the 400 an empty/JSON body would raise during serializer validation.""" + content_url, repos_url, owner_header = configure_lightwell_domain + # The upload create policy requires a repository; reuse the one the owner created in the + # fixture. The serializer's repository field is backed by Repository.objects.all(), so the + # href resolves for any authenticated caller -- it's the per-repo modify permission the + # entitled-but-unroled caller lacks that produces the 403. + repos = requests.get(repos_url, headers={"x-rh-identity": owner_header}, timeout=30).json() + repo_href = repos["results"][0]["pulp_href"] headers = {"x-rh-identity": _identity_header(LIGHTWELL_ENTITLED_ORG_ID, "entitled-post-user")} - response = requests.post(content_url, headers=headers, json={}, timeout=30) + response = requests.post( + content_url, + headers=headers, + files={"file": ("test.txt", b"test content")}, + data={"relative_path": "test.txt", "repository": repo_href}, + timeout=30, + ) - assert response.status_code in (400, 401, 403) + assert response.status_code == 403 @pytest.fixture