[Element] Add hasViewAccess flag to RelatedElementData based on view permission - #1983
[Element] Add hasViewAccess flag to RelatedElementData based on view permission#1983xIrusux wants to merge 5 commits into
Conversation
…ission Relation rows carried no permission information, so the Studio UI rendered per-row actions (e.g. Open) regardless of whether the current user is allowed to view the referenced element. Compute hasAccess in ElementDataService through the same GDI ElementPermissionService seam that SecurityService uses for element permission checks, denying by default when no user can be resolved. Reworked from #1872, addressing the review feedback there: the service stays final, no canEdit / RelationNormalizationContext (parent save/publish gating already exists in the UI edit form), and no GridSearch workspace bypass. Enables pimcore/studio-ui-bundle#3898. Co-authored-by: Jonathon Meney <253076990+Jonathon-Meney-Torq@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds hasAccess to related-element responses based on the current user’s view permission.
Changes:
- Extends the related-element schema with
hasAccess. - Resolves view access through the GDI permission service.
- Tests allowed, denied, and unauthenticated scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/Element/Schema/RelatedElementData.php |
Adds the access flag. |
src/Element/Service/ElementDataService.php |
Computes view access. |
tests/Unit/Element/Service/ElementDataServiceTest.php |
Covers access outcomes. |
…nsform getRelatedElementData() runs inside the relation row loops, and the GDI permission seam normalizes the whole element per call (all class fields, including lazy loads), which can make object-detail requests prohibitively slow for field-heavy classes with many relation rows. Switch to the core AbstractElement::isAllowed() check: one cheap workspace lookup per row, no transform. Also split the 123-char Property annotation line flagged by Sonar. Co-authored-by: Jonathon Meney <253076990+Jonathon-Meney-Torq@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Concrete example: a relation field with 20 Car rows, where Car itself has several lazy relations and localized fields → 20 × (full field walk + those lazy loads). That can easily be 100+ queries for one field that previously cost zero. The real cost, per data-object relation row
Options for the team discussion (ranked as I'd argue them)
Update: 84d5f65 switches the implementation to option 2 (core |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/Element/Service/ElementDataService.php:64
- This contradicts the stated contract that
hasAccessuses the same GDI permission seam as the get/open endpoints. Those endpoints callSecurityService::hasElementPermission()(ElementService.php:85,107), which delegates toElementPermissionServiceInterface, while this invokes Pimcore's coreisAllowed()path (including different workflow/event behavior). The two results can therefore diverge, so the UI may disable an element that the endpoint allows, or expose an Open action that still returns 403. Use the endpoint permission path here (mappingForbiddenExceptiontofalse) and update the test to mock that seam, or explicitly revise the PR's contract if classic semantics are intentional.
// Intentionally the core check, not the GDI permission seam: this runs per relation row,
// and the GDI check normalizes the whole element per call (see PR #1983).
return $element->isAllowed(ElementPermissions::VIEW_PERMISSION, $user);
The flag specifically reports the view permission; the precise name keeps it unambiguous next to a possible future edit-side flag and matches the existing permission vocabulary (Permissions schema, UserWorkspace::hasView()). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|



Summary
hasViewAccessboolean to theRelatedElementDataschema (defaulttrue), telling theclient whether the current user is allowed to view the referenced element.
ElementDataServicecomputes the flag via the core$element->isAllowed('view', $user)check —one indexed workspace query per row, no per-row element normalization (see performance note below).
Enables pimcore/studio-ui-bundle#3898 (gate the per-row Open button on
hasViewAccess).Relation to #1872
Reworked from #1872 (thanks @Jonathon-Meney-Torq!), addressing the review feedback there:
ElementDataServicestaysfinal readonly— no subclassing needed.GridSearchchange — skipping workspace/user restrictions forsystem.idsfilterqueries would let any authenticated user read grid data for arbitrary elements outside their
workspace; the endpoint cannot verify the IDs actually come from a relation the user is
viewing. Dropped entirely.
canEdit/RelationNormalizationContext— the Remove action edits the parentobject, and the Studio UI already disables it via the parent's save/publish permissions
(
edit-form-provider), so no per-row flag and no mutable normalization-context service isneeded.
Performance note (resolved)
The first iteration computed the flag through the GDI
ElementPermissionServiceInterface, but thattransforms the full element per call —
DataObjectNormalizer::normalizeStandardFields()walks everyfield definition and triggers lazy loads, per relation row. Since this runs on the object-detail hot
path, 84d5f65 switched to the core
isAllowed()check: one cheap indexedusers_workspaces_objectquery per row, constant cost regardless of class complexity, with precedent in
WorkflowElementsService. Actual security enforcement stays at the (GDI-based) element endpoints —this flag is a rendering hint.
The unit test mocks the seam, so a later switch to an index-batched approach stays cheap.
Naming
The flag is named
hasViewAccess(nothasAccess) to state precisely which permission it reports,matching the existing vocabulary (
Permissionsschema,UserWorkspace::hasView()) and keeping thename unambiguous next to a possible future edit-side flag.
🤖 Generated with Claude Code