Skip to content

read only group and extra logging#1307

Merged
YasenT merged 3 commits into
pulp:mainfrom
YasenT:fix/lightwell-feature-guard-latency
Jul 3, 2026
Merged

read only group and extra logging#1307
YasenT merged 3 commits into
pulp:mainfrom
YasenT:fix/lightwell-feature-guard-latency

Conversation

@YasenT

@YasenT YasenT commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

Introduce a hardcoded Lightwell read-only group and improve Lightwell PyPI access logging and permission handling.

New Features:

  • Add a hardcoded Lightwell-ReadOnly group whose members receive read-only access to the lightwell domain's non-PyPI endpoints, independent of any DomainOrg association.

Enhancements:

  • Log Lightwell PyPI access decisions with explicit allow/deny reasons and org/user context.
  • Refactor safe-method permission handling into a shared helper that incorporates public domains, PyPI views, and the Lightwell read-only group.
  • Extend domain scoping so Lightwell read-only group members can see the lightwell domain in domain listings.

Documentation:

  • Document the Lightwell-ReadOnly group behavior in the changelog entry.

Tests:

  • Add unit tests covering Lightwell PyPI access logging and the Lightwell read-only group permission behavior.
  • Add functional tests validating Lightwell read-only group behavior across repository listing, writes, PyPI views, other domains, and domain listings.

@sourcery-ai

sourcery-ai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduces a hardcoded Lightwell read-only group with special handling in DomainBasedPermission for non-PyPI lightwell endpoints, adds detailed PyPI access logging, and provides unit and functional tests plus release notes to validate and document the behavior.

File-Level Changes

Change Details Files
Add detailed logging around Lightwell PyPI read-access decisions so incidents can be diagnosed from logs.
  • Log when lightwell PyPI access is granted via DomainOrg association, including user and org_id.
  • Log denials when org_id is missing from the identity header and no DomainOrg match exists.
  • Wrap the lightwell-network feature check in a logging block that records whether access was granted or denied, along with org_id and user.
  • Preserve existing feature-check behavior while surfacing decisions through the pulp_service.app.authorization logger at INFO level.
pulp_service/pulp_service/app/authorization.py
pulp_service/pulp_service/tests/unit/test_domain_based_permission.py
Introduce a hardcoded Lightwell read-only group that grants SAFE_METHOD read access to the lightwell domain's non-PyPI endpoints without DomainOrg association, wired into DomainBasedPermission and queryset scoping.
  • Define LIGHTWELL_READONLY_GROUP_NAME constant with documentation on scope and limitations.
  • Implement _has_lightwell_readonly_group_access to check authenticated membership in the hardcoded group via user.groups.filter().
  • Refactor SAFE_METHOD handling into _check_safe_method_access to centralize public-domain, PyPI, and Lightwell read-only group shortcuts.
  • Update has_permission to delegate SAFE_METHOD logic to _check_safe_method_access, falling back to standard DomainOrg checks when it returns None.
  • Extend scope_queryset to include the lightwell domain in listings when the user has read-only group membership, in addition to existing DomainOrg and org_id-based filters.
pulp_service/pulp_service/app/authorization.py
Add unit tests for Lightwell PyPI access logging and read-only group semantics at the permission level.
  • Extend the authenticated user test helper to simulate Lightwell read-only group membership using user.groups.filter().exists().
  • Add TestLightwellPyPIAccessLogging test class to verify logging for DomainOrg grants, missing org_id denials, feature-check grants, and feature-check denials.
  • Add TestLightwellReadOnlyGroupAccess test class to validate that read-only group members can GET non-PyPI lightwell resources but cannot write, do not bypass PyPI feature checks, and gain no access to other domains.
  • Keep existing TestSafeMethodDenied tests intact after inserting the new test classes.
pulp_service/pulp_service/tests/unit/test_domain_based_permission.py
Add functional tests to validate Lightwell read-only group behavior end-to-end against a Pulp instance.
  • Create fixtures to set up the literal "lightwell" domain with file and PyPI repositories and to construct x-rh-identity headers for different orgs/users.
  • Add a fixture to create users who are members of the hardcoded read-only group via pulpcore bindings and return their identity headers.
  • Verify that read-only group members can list repositories in the lightwell domain while non-members receive 403 responses.
  • Ensure read-only group members cannot create repositories (write denied) and still require the lightwell-network feature for PyPI views.
  • Confirm that read-only group membership does not grant access to other domains and that such members see the lightwell domain in /domains/ listings via DomainBasedPermission.scope_queryset().
pulp_service/pulp_service/tests/functional/test_lightwell_readonly_group_permission.py
Document the Lightwell read-only group feature in the changelog.
  • Add a feature entry describing the Lightwell-ReadOnly group, its read-only scope over the lightwell domain's non-PyPI endpoints, its independence from DomainOrg, and its lack of write/PyPI bypass capability.
CHANGES/lightwell-readonly-group.feature

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

…p changes

Co-authored-by: Cursor <cursoragent@cursor.com>

@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 1 issue

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

## Individual Comments

### Comment 1
<location path="pulp_service/pulp_service/tests/functional/test_lightwell_readonly_group_permission.py" line_range="176-178" />
<code_context>
+    assert response.status_code == 403
+
+
+def test_readonly_group_member_write_denied(configure_lightwell_domain, gen_readonly_group_member):
+    """The read-only group grants no write access -- a member must still be denied when
+    trying to create a repository in the lightwell domain."""
+    repos_url, _, _ = configure_lightwell_domain
+    headers = {"x-rh-identity": gen_readonly_group_member("write")}
+
+    response = requests.post(repos_url, headers=headers, json={"name": str(uuid4())}, timeout=30)
+
+    assert response.status_code in (401, 403)
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** Consider tightening the assertion to explicitly expect a 403, to prove this is an authorization failure rather than an authentication issue.

This test currently accepts either 401 or 403 for a write attempt by a read-only group member. Since we want to validate that DomainBasedPermission is denying write access (rather than a missing/invalid identity), please assert specifically on 403, and optionally assert on a permission-specific error code in the response body if available, to make the authorization behavior explicit.

```suggestion
    response = requests.post(repos_url, headers=headers, json={"name": str(uuid4())}, timeout=30)

    assert response.status_code == 403
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +176 to +178
response = requests.post(repos_url, headers=headers, json={"name": str(uuid4())}, timeout=30)

assert response.status_code in (401, 403)

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.

suggestion (testing): Consider tightening the assertion to explicitly expect a 403, to prove this is an authorization failure rather than an authentication issue.

This test currently accepts either 401 or 403 for a write attempt by a read-only group member. Since we want to validate that DomainBasedPermission is denying write access (rather than a missing/invalid identity), please assert specifically on 403, and optionally assert on a permission-specific error code in the response body if available, to make the authorization behavior explicit.

Suggested change
response = requests.post(repos_url, headers=headers, json={"name": str(uuid4())}, timeout=30)
assert response.status_code in (401, 403)
response = requests.post(repos_url, headers=headers, json={"name": str(uuid4())}, timeout=30)
assert response.status_code == 403

Ruff's ARG001 check flagged the unused fixture in changed lines.

Co-authored-by: Cursor <cursoragent@cursor.com>
@YasenT YasenT merged commit 0f236fc into pulp:main Jul 3, 2026
3 of 6 checks passed
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