Skip to content

Add Content View patches for cross-domain search testing - #1374

Open
YasenT wants to merge 2 commits into
pulp:mainfrom
YasenT:contentview
Open

Add Content View patches for cross-domain search testing#1374
YasenT wants to merge 2 commits into
pulp:mainfrom
YasenT:contentview

Conversation

@YasenT

@YasenT YasenT commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Wires the new pulpcore ContentView resource (patch 0062) and pulp_rpm's six nested RPM search endpoints built on it (patch 0063) into the Dockerfile so they can be exercised against pulp-service's pinned pulpcore/pulp_rpm versions. Also adds a DomainOrgAuthenticationBackend so that user.has_perm("core.view_domain", obj=domain) checks -- used by ContentView's cross-domain distribution resolution -- reflect DomainOrg membership for domains outside of the current request's scope, which pulp-service's request-scoped DomainBasedPermission alone can't answer.

Verified end-to-end in the ghcr.io/pulp/hosted-pulp-dev-env:main dev container via pulp-add-patch/pulp-restart: CRUD, all 6 search endpoints aggregating real RPM content synced across two domains, and RBAC exclusion of a domain a limited user lacks DomainOrg access to.

Summary by Sourcery

Integrate new pulpcore ContentView resource and RPM cross-domain search endpoints into the hosted Pulp environment, and ensure domain-level RBAC works for cross-domain access checks.

New Features:

  • Expose pulpcore ContentView resource and nested RPM search endpoints via Docker patches so they are available in the hosted dev and service environments.

Enhancements:

  • Add DomainOrgAuthenticationBackend to answer core.view_domain object-level permission checks based on DomainOrg membership outside the request scope.
  • Update authentication backend configuration for deployed and dev-container environments to include DomainOrgAuthenticationBackend.
  • Document the new ContentView and RPM cross-domain search patches and their behavior in CHANGES and CLAUDE patch metadata.

Build:

  • Wire new pulpcore and pulp_rpm patches (0062, 0063) into the Dockerfile so ContentView and RPM search functionality are applied at build time.
  • Refresh pinned pulpcore and bundled pulp_file versions in patch metadata to align with patched ContentView support.

Documentation:

  • Add a CHANGES feature entry and extend CLAUDE patch documentation to describe the new ContentView resource and RPM cross-domain search endpoints.

Tests:

  • Add unit tests covering DomainOrgAuthenticationBackend.has_perm behavior for direct user, group-based, and unauthenticated or invalid access scenarios.

Wires the new pulpcore ContentView resource (patch 0062) and pulp_rpm's
six nested RPM search endpoints built on it (patch 0063) into the
Dockerfile so they can be exercised against pulp-service's pinned
pulpcore/pulp_rpm versions. Also adds a DomainOrgAuthenticationBackend so
that user.has_perm("core.view_domain", obj=domain) checks -- used by
ContentView's cross-domain distribution resolution -- reflect DomainOrg
membership for domains outside of the current request's scope, which
pulp-service's request-scoped DomainBasedPermission alone can't answer.

Verified end-to-end in the ghcr.io/pulp/hosted-pulp-dev-env:main dev
container via pulp-add-patch/pulp-restart: CRUD, all 6 search endpoints
aggregating real RPM content synced across two domains, and RBAC
exclusion of a domain a limited user lacks DomainOrg access to.

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

sourcery-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds support for pulpcore’s new ContentView resource and pulp_rpm’s nested cross-domain RPM search endpoints to the dev/Docker environment, and introduces a DomainOrgAuthenticationBackend so ContentView’s cross-domain domain permission checks honor DomainOrg membership outside the current request scope.

Sequence diagram for ContentView cross-domain core.view_domain checks via DomainOrgAuthenticationBackend

sequenceDiagram
    actor DjangoUser
    participant ContentView
    participant DomainOrgAuthenticationBackend
    participant Domain
    participant DomainOrg

    ContentView->>DjangoUser: has_perm(core.view_domain, domain)
    DjangoUser->>DomainOrgAuthenticationBackend: has_perm(user_obj, perm, obj)
    DomainOrgAuthenticationBackend->>DomainOrgAuthenticationBackend: [perm != core.view_domain or obj not Domain]
    DomainOrgAuthenticationBackend-->>DjangoUser: False
    opt [perm == core.view_domain and obj is Domain and user_obj.is_authenticated]
        DomainOrgAuthenticationBackend->>DomainOrg: objects.filter(domains__pk=obj.pk, user=user_obj)
        DomainOrgAuthenticationBackend->>DomainOrgAuthenticationBackend: group_pks = user_obj.groups.values_list(pk)
        DomainOrgAuthenticationBackend->>DomainOrg: objects.filter(domains__pk=obj.pk, group_id__in=group_pks)
        DomainOrg->>DomainOrgAuthenticationBackend: exists()
        DomainOrgAuthenticationBackend-->>DjangoUser: True/False
    end
Loading

File-Level Changes

Change Details Files
Introduce a DomainOrgAuthenticationBackend to answer core.view_domain object permissions based on DomainOrg membership outside the request cycle, and wire it into Pulp’s authentication backend configuration.
  • Import BaseBackend from django.contrib.auth.backends in the authorization module.
  • Implement DomainOrgAuthenticationBackend.has_perm to short-circuit on incorrect perm, missing object, non-Domain object, or unauthenticated user.
  • Evaluate DomainOrg associations via direct user or group membership with a Q query against DomainOrg.objects.
  • Register DomainOrgAuthenticationBackend in dev-container settings and clowdapp PULP_AUTHENTICATION_BACKENDS so it is active in both dev and deployed environments.
  • Add unit tests validating has_perm behavior for positive and negative cases using mocked DomainOrg.objects and MagicMock users.
pulp_service/pulp_service/app/authorization.py
deploy/clowdapp.yaml
dev-container/settings.py
pulp_service/pulp_service/tests/unit/test_domain_org_authentication_backend.py
Wire new pulpcore ContentView resource and pulp_rpm RPM Content View search endpoints patches into the Docker image and document them, including updated pulpcore/pulp_rpm versions and release notes.
  • Update CLAUDE.md to bump pulpcore/pulp_file to 3.115.2, add pulp_rpm 3.38.2, and document patches 0062 and 0063 (ContentView resource and RPM search endpoints).
  • Copy and apply patch 0062 (Add Content View resource) in the Dockerfile so pulpcore’s ContentView model, serializers, viewsets, and utilities are available in the container.
  • Copy and apply patch 0063 (Add RPM Content View search endpoints) in the Dockerfile so the six nested RPM search endpoints are wired into pulp_rpm.
  • Add a CHANGES entry describing the application of ContentView and RPM cross-domain search patches and the DomainOrgAuthenticationBackend behavior.
  • Add placeholder patch files for 0062 and 0063 under images/assets/patches to be consumed by the Dockerfile build process.
images/assets/patches/CLAUDE.md
Dockerfile
CHANGES/content-view-cross-domain-access.feature
images/assets/patches/0062-Add-Content-View-resource.patch
images/assets/patches/0063-Add-RPM-Content-View-search-endpoints.patch

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 1 issue, and left some high level feedback:

  • In DomainOrgAuthenticationBackend.has_perm, consider short-circuiting for superusers (e.g. user_obj.is_superuser) so global admins are not constrained by DomainOrg associations when resolving core.view_domain.
  • The strict isinstance(obj, Domain) check in DomainOrgAuthenticationBackend.has_perm may block permission evaluation for proxy or subclassed Domain models; if those are possible, loosening this check (e.g. by type attribute or using Domain._meta.concrete_model) would make the backend more robust.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `DomainOrgAuthenticationBackend.has_perm`, consider short-circuiting for superusers (e.g. `user_obj.is_superuser`) so global admins are not constrained by `DomainOrg` associations when resolving `core.view_domain`.
- The strict `isinstance(obj, Domain)` check in `DomainOrgAuthenticationBackend.has_perm` may block permission evaluation for proxy or subclassed `Domain` models; if those are possible, loosening this check (e.g. by type attribute or using `Domain._meta.concrete_model`) would make the backend more robust.

## Individual Comments

### Comment 1
<location path="pulp_service/pulp_service/tests/unit/test_domain_org_authentication_backend.py" line_range="68-75" />
<code_context>
+
+        assert backend.has_perm(None, "core.view_domain", obj=domain) is False
+
+    @patch("pulp_service.app.authorization.DomainOrg.objects")
+    def test_direct_user_association_grants(self, mock_domain_org):
+        mock_domain_org.filter.return_value.exists.return_value = True
+        backend = DomainOrgAuthenticationBackend()
+        user = _make_authenticated_user()
+        domain = _make_domain()
+
+        assert backend.has_perm(user, "core.view_domain", obj=domain) is True
+        mock_domain_org.filter.assert_called_once()
+
</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen assertions to verify the ORM filter is constructed with the expected user/domain constraints

Currently this only checks that `DomainOrg.objects.filter` is called once. To better verify the permission logic, assert the exact arguments passed to `filter` (e.g., includes the domain PK and `user`, and omits `group_id__in` when `group_pks` is empty). This will ensure future changes don’t alter these constraints while still calling `filter`.

Suggested implementation:

```python
        assert backend.has_perm(user, "core.view_domain", obj=domain) is True

        mock_domain_org.filter.assert_called_once()
        args, kwargs = mock_domain_org.filter.call_args

        # No positional args should be used in the filter call
        assert args == ()

        # Ensure the filter is constrained by the domain and user
        assert kwargs.get("domain_id") == domain.pk
        assert kwargs.get("user") == user

        # When there are no groups, group-based constraints should not be added
        assert "group_id__in" not in kwargs

```

If the actual implementation of `DomainOrgAuthenticationBackend` uses different keyword names (e.g. `domain` instead of `domain_id`, or `user_id` instead of `user`), adjust the `kwargs.get(...)` keys accordingly so they match the real ORM filter signature while keeping the assertion that `group_id__in` is absent.
</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 +68 to +75
@patch("pulp_service.app.authorization.DomainOrg.objects")
def test_direct_user_association_grants(self, mock_domain_org):
mock_domain_org.filter.return_value.exists.return_value = True
backend = DomainOrgAuthenticationBackend()
user = _make_authenticated_user()
domain = _make_domain()

assert backend.has_perm(user, "core.view_domain", obj=domain) is True

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): Strengthen assertions to verify the ORM filter is constructed with the expected user/domain constraints

Currently this only checks that DomainOrg.objects.filter is called once. To better verify the permission logic, assert the exact arguments passed to filter (e.g., includes the domain PK and user, and omits group_id__in when group_pks is empty). This will ensure future changes don’t alter these constraints while still calling filter.

Suggested implementation:

        assert backend.has_perm(user, "core.view_domain", obj=domain) is True

        mock_domain_org.filter.assert_called_once()
        args, kwargs = mock_domain_org.filter.call_args

        # No positional args should be used in the filter call
        assert args == ()

        # Ensure the filter is constrained by the domain and user
        assert kwargs.get("domain_id") == domain.pk
        assert kwargs.get("user") == user

        # When there are no groups, group-based constraints should not be added
        assert "group_id__in" not in kwargs

If the actual implementation of DomainOrgAuthenticationBackend uses different keyword names (e.g. domain instead of domain_id, or user_id instead of user), adjust the kwargs.get(...) keys accordingly so they match the real ORM filter signature while keeping the assertion that group_id__in is absent.

@YasenT

YasenT commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

/retest

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