Skip to content

Return Basic realm from CONTENT_ORIGIN in WWW-Authenticate header - #1640

Merged
git-hyagi merged 1 commit into
pulp:mainfrom
git-hyagi:header-content-guard-www-auth
Oct 23, 2026
Merged

git-hyagi merged 1 commit into
pulp:mainfrom
git-hyagi:header-content-guard-www-auth

Conversation

@git-hyagi

@git-hyagi git-hyagi commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

The OrgGroupAssignmentMixin.authenticate_header() was returning a static "Bearer" value. When DRF denied an unauthenticated request to a content-guarded PyPI view, the 401 response carried WWW-Authenticate: Bearer instead of a Basic challenge that prompts browsers for credentials.

Derive the realm from settings.CONTENT_ORIGIN so the header matches the environment (e.g. packages.redhat.com vs packages.stage.redhat.com).

ref: https://redhat.atlassian.net/browse/PULP-2594

Summary by Sourcery

Use the configured content origin to generate browser-compatible WWW-Authenticate Basic challenges.

Bug Fixes:

  • Return a Basic authentication challenge with an environment-specific realm for unauthenticated content-guarded requests.

Tests:

  • Update authentication tests to verify realms derived from the configured content origin.

@sourcery-ai

sourcery-ai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The PR replaces the static Bearer challenge with a Basic challenge whose realm is derived from CONTENT_ORIGIN, including a production-host fallback, and updates the authentication error test to provide the required setting.

Sequence diagram for the CONTENT_ORIGIN Basic authentication challenge

sequenceDiagram
    participant Client
    participant DRF
    participant Auth as OrgGroupAssignmentMixin
    participant Settings

    Client->>DRF: Unauthenticated content-guarded request
    DRF->>Auth: authenticate_header(request)
    Auth->>Settings: CONTENT_ORIGIN
    Auth->>Auth: urlparse(CONTENT_ORIGIN)
    alt CONTENT_ORIGIN has a hostname
        Auth-->>DRF: Basic realm=hostname
    else hostname is missing
        Auth-->>DRF: Basic realm=packages.redhat.com
    end
    DRF-->>Client: 401 with WWW-Authenticate header
Loading

File-Level Changes

Change Details Files
Generate a Basic authentication challenge whose realm reflects the configured content origin.
  • Parse the hostname from settings.CONTENT_ORIGIN.
  • Fallback to the production package hostname when the configured origin has no hostname.
  • Return the realm in the WWW-Authenticate challenge using Basic authentication syntax.
pulp_service/pulp_service/app/authentication.py
Update authentication error coverage to configure the content origin used by the challenge logic.
  • Inject Django settings into the test and set CONTENT_ORIGIN to the production HTTPS URL.
  • Retain verification that content-guard authentication failures map to HTTP 401 responses.
pulp_service/pulp_service/tests/unit/test_authentication_error_content_guard.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="pulp_service/pulp_service/app/authentication.py" line_range="84" />
<code_context>
     def authenticate_header(self, request):
-        return "Bearer"
+        hostname = urlparse(settings.CONTENT_ORIGIN).hostname or "packages.redhat.com"
+        return f'Basic realm="{hostname}"'


</code_context>
<issue_to_address>
**Guarded PyPI tests fail**

When the guarded-PyPI functional test runs, `test_unauthenticated_denied_on_guarded_pypi_simple_api` expects `WWW-Authenticate` to equal `Bearer`, but `authenticate_header()` returns a Basic challenge, so the assertion fails and the functional suite is red.

Update the functional test to assert the intended Basic challenge.

Also at `pulp_service/pulp_service/app/authentication.py:85`.
</issue_to_address>

### Comment 2
<location path="pulp_service/pulp_service/tests/unit/test_authentication_error_content_guard.py" line_range="97" />
<code_context>
+    def test_permit_maps_authentication_error_to_401(self, settings):
         detail_guard = _detail_guard("identity", AuthenticationError("Authentication required."))
         request = MagicMock(path="/lightwell/osv/record")
+        settings.CONTENT_ORIGIN = "https://packages.redhat.com"

         with pytest.raises(HTTPUnauthorized) as exc_info:
</code_context>
<issue_to_address>
**Static realm passes the test**

When the realm implementation stops deriving its hostname from `CONTENT_ORIGIN` and always uses the `packages.redhat.com` fallback, `test_permit_maps_authentication_error_to_401` sets `CONTENT_ORIGIN` to `packages.redhat.com` at line 97 and checks that same hostname at line 103, so a constant `Basic realm="packages.redhat.com"` passes even when other environments receive the wrong realm.

Set `CONTENT_ORIGIN` to a different hostname in this test and assert that hostname appears in the challenge.
</issue_to_address>

Sourcery assessment

Approval pending. 2 findings to address first.

Blocking findings: pulp_service/pulp_service/app/authentication.py:84, pulp_service/pulp_service/tests/unit/test_authentication_error_content_guard.py:97


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

The OrgGroupAssignmentMixin.authenticate_header() was returning a static
"Bearer" value. When DRF denied an unauthenticated request to a
content-guarded PyPI view, the 401 response carried
WWW-Authenticate: Bearer instead of a Basic challenge that prompts
browsers for credentials.

Derive the realm from settings.CONTENT_ORIGIN so the header matches the
environment (e.g. packages.redhat.com vs packages.stage.redhat.com).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@git-hyagi
git-hyagi force-pushed the header-content-guard-www-auth branch from 98bbc43 to 4b93fd6 Compare October 23, 2026 15:09
@git-hyagi
git-hyagi merged commit 2e2a0d3 into pulp:main Oct 23, 2026
6 checks passed
@git-hyagi
git-hyagi deleted the header-content-guard-www-auth branch October 23, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant