Conversation
GetPermissionsAsync was querying only explicit per-node permissions, ignoring the ancestor-based inheritance model. Nodes without explicit permissions would get group defaults instead of inheriting from their nearest ancestor with explicit permissions. This caused tree filtering to hide child nodes that should have been visible. Replace per-node permission queries with GetPermissionsForPath which walks the entity path to resolve inherited permissions correctly. Also pass object types through to enable batched entity lookups.
There was a problem hiding this comment.
Pull request overview
Fixes permission resolution for document/media nodes so permissions are inherited from the nearest ancestor with explicit permissions (instead of falling back to group defaults when the node itself has no explicit permissions). This aligns permission checks with how the content tree should be filtered/visible in the backoffice.
Changes:
- Update
UserServicedocument/media permission resolution to use path-based permission inheritance viaGetPermissionsForPath. - Add integration tests validating inheritance/override behavior and unknown-user handling.
- Split
UserServiceTestsinto partial files and wire the new test file into the integration test project.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj | Includes the new partial test file under UserServiceTests for compilation/IDE nesting. |
| tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/UserServiceTests.cs | Marks the fixture as partial to support splitting tests across files. |
| tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/UserServiceTests.Permissions.cs | Adds 5 integration tests covering inherited permissions, override behavior, nearest-ancestor selection, and unknown user. |
| src/Umbraco.Core/Services/UserService.cs | Switches document/media permission resolution to path-based inheritance using entity paths. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…to v17/bugfix/fix-tree-permissions-filtering
There was a problem hiding this comment.
This fix looks good to me @lauraneto, nice catch. I've tested it out with the steps you describe and can see the original problem and confirm that this resolves it. The integration tests provide good and sufficient coverage too.
I did want to have a look at the performance concern raised in the Copilot review, as I've been doing a bit of work recently looking to avoid these types of "N+1" queries (see #21684). Of course correct and a bit slower is better than fast and wrong! But I think there's a way to do similar here, so have done so.
I made a benchmark test that seems like it's realistic, and got some good numbers:
Before:
GetDocumentPermissionsAsync_Performance_50_SiblingsGetDocumentPermissionsAsync — 50 children, 5 iterations:
median=86ms, min=74ms, max=93ms, all=[74ms, 92ms, 93ms, 86ms, 84ms]
After:
GetDocumentPermissionsAsync_Performance_50_SiblingsGetDocumentPermissionsAsync — 50 children, 5 iterations:
median=8ms, min=7ms, max=10ms, all=[7ms, 10ms, 9ms, 7ms, 8ms]
The integration tests continues to pass and the manual check looks OK, so I think it's good. But perhaps you could have a look over to see if you see any concerns and agree with the approach before we merge this.
|
@AndyButland Thank you for checking, and amazing work on the performance improvements!
|
|
Great, those two additional tests are useful to have. Feel free to merge this in if you are happy with it, it all looks good to me. |
Summary
GetPermissionsAsyncwas querying only explicit per-node permissions, ignoring ancestor-based permission resolution. Nodes without explicit permissions would get group defaults instead of inheriting from their nearest ancestor with explicit permissions. This caused tree filtering to hide child nodes that should have been visible (e.g. documents created in a folder were not appearing in the tree).GetPermissionsForPathwhich walks the entity path to resolve permissions from the nearest ancestor. Also pass object types through to enable batched entity lookups viaGetAll.Testing
To reproduce the original issue:
The Parent's children endpoint would return 0 results, making the child document invisible in the tree. But if you try to access the child's url directly you can view it.
After this fix, child documents correctly resolve permissions from their nearest ancestor and appear as expected.