Add ContentView resource with cross-domain scatter/gather utility - #7958
Add ContentView resource with cross-domain scatter/gather utility#7958YasenT wants to merge 3 commits into
Conversation
Introduces a new first-class Pulp resource, ContentView: a named, domain-scoped object composed of Distributions that may span multiple domains, with full CRUD and RBAC. Plugins build cross-domain search endpoints on top of it using the new resolve_content_view_distributions/ group_versions_by_domain/scatter_gather utilities, exposed via pulpcore.plugin.*, without querying the database directly or bypassing RBAC. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
A plugin may register an additional read-only, nested viewset that reuses an existing content type's queryset for its own purposes (e.g. the RPM ContentView search endpoints reusing Package/UpdateRecord/etc.) without intending to compete for that model's canonical viewset. Since such viewsets are always nested (they declare parent_viewset), exclude them from the ambiguity check when exactly one non-nested candidate remains. Without this fix, registering a second viewset against an existing content model made get_viewset_for_model raise LookupError for that model unconditionally, breaking RepositoryVersion content_summary hrefs and master-viewset queryset scoping for every affected content type. Co-authored-by: Cursor <cursoragent@cursor.com>
|
While I see that this particular user has this as a standard workflow - it violates the basic promise Pulp makes with domains, which is "no user gets access to a domain's content that they don't belong to". Even under RBAC, this extends the attack-surface for violating that promise quite widely. I don't see this being something any Pulp installation other than Content Sources wanting. At an absolute minimum, I'd want a way for an installation to turn this off, and have that be the default. This "feels like" it changes The Rules in ways that make it better-suited to being its own specific plugin, which an installation can choose to include, or not. But I don't think it belongs in the base project code. |
|
@ggainey Thank you for this, we agreed internally to keep it part of pulp-service only :) |
Add ContentView resource with cross-domain scatter/gather utility
Problem
Content Sources (via
tang) performs batch searches across repository versions spanning multiple domains by opening a rawpgxconnection straight to Pulp's Postgres. This has three problems:Content Sources templates routinely mix repositories from a private org domain and shared/public domains, so cross-domain search is a first-class requirement, not an edge case.
What this adds
A new first-class Pulp resource,
ContentView— a named, persistable object that composes distributions from multiple domains into a single searchable scope, replacing the raw-hrefs-per-request approach with a proper Pulp resource.ContentView): domain-scoped like any other Pulp resource, but itsdistributionsManyToManyFieldcan referenceDistributions from any domain the user has read access to. Each distribution already carries version-tracking semantics (tracks latest repo, pinned version, or a publication), so the content view doesn't need to duplicate that.ContentViewViewSetextendsNamedModelViewSet/RolesMixin/LabelsMixinwith a standardDEFAULT_ACCESS_POLICY/LOCKED_ROLES, following existing pulpcore conventions. Any user in the same org can read/update/delete content views in domains they have access to.pulpcore.app.util_content_view, exported viapulpcore.plugin.util):resolve_content_view_distributions,group_versions_by_domain, andscatter_gatherlet plugins resolve a content view's distributions to their current repository versions, group them by domain, and run per-domain queries that are merged in Python — the single-domain case collapses to one query, multi-domain does a bounded per-domain over-fetch.user_can_view_domainrespects domain-levelcore.view_domainpermission checks during resolution, so a distribution the user has lost access to is silently excluded from search results rather than erroring; the content view detail endpoint'sdistributions_statusfield reports per-distribution status (ok,no_domain_access, deleted, etc.) so callers can see what's excluded and why.router_lookup = "content_view"onContentViewViewSetlets plugins (e.g.pulp_rpm) register read-only search viewsets nested under/content-views/{uuid}/search/...viarest_framework_nested.routers, reusing an existing content type's queryset without competing for that model's canonical viewset.get_viewset_for_modelpreviously raisedLookupErrorwhen a model had more than one registered viewset (its canonical viewset plus a plugin's nested, read-only search viewset reusing the same queryset) — a real bug hit during RPM sync once a plugin registers a nested search viewset. Fixed by disambiguating: if excluding nested viewsets (those with aparent_viewset) leaves exactly one candidate, that's the canonical viewset.Out of scope (this PR)
search/rpm/*) — implemented separately inpulp_rpmon top of this.Testing
scatter_gather(single- and multi-domain paths), including RBAC exclusion scenarios.get_viewset_for_modeldisambiguation fix, covering both the resolvable and genuinely-ambiguous cases.pulp_rpmsearch endpoints.