⚠️ This issue respects the following points: ⚠️
Bug description
group_restrictions on a calendar room or resource are enforced in some of AbstractPrincipalBackend's read paths but not in others. As a result a restricted room stays visible to users outside the allowed group in Nextcloud Calendar's "Show rooms" dialog, while the attendee type-ahead correctly hides it.
isAllowedToAccessResource() is called from searchPrincipals() and findByUri(), but not from getPrincipalsByPrefix() or getPrincipalByPath() — those two do not even select the group_restrictions column:
| Method |
Selects group_restrictions |
Calls isAllowedToAccessResource() |
getPrincipalsByPrefix() (L70) |
no |
no |
getPrincipalByPath() (L121) |
no |
no |
searchPrincipals() (L204) |
yes |
yes |
findByUri() (L400) |
yes |
yes |
Calendar fills the "Show rooms" dialog via fetchRoomAndResourcePrincipals(), which does a PROPFIND on dav/principals/calendar-rooms/ — i.e. straight through getPrincipalsByPrefix().
Only the room's name, email and metadata leak; booking is still rejected server-side. But room names and locations can reveal organisational structure, which is exactly what the restriction is meant to prevent.
Steps to reproduce
- Have a room backend providing at least one room (any app implementing
IRoomBackend).
- Give that room a
group_restrictions value, e.g. ["test"], and let the hourly UpdateCalendarResourcesRoomsBackgroundJob write it to oc_calendar_rooms.
- Log in as a user who is not in that group.
- Create an event in Calendar → Resources → Show rooms.
Expected behaviour
The restricted room is not listed, consistent with the attendee type-ahead.
Actual behaviour
The room is listed with its name, email and metadata.
Measured directly against the backend on a live NC 34 instance, with two test accounts differing only in group membership:
user groups searchPrincipals getPrincipalsByPrefix
roomtest-in [test] 1 1
roomtest-uit [] 0 1 <-- should be 0
Setting the restriction back to [] makes both users see the room again through both paths, which confirms the restriction itself is stored and evaluated correctly — one of the two read paths simply skips the check.
Note for whoever picks this up
AbstractPrincipalBackendTestCase::testGetPrincipalsByPrefix() currently asserts that all six fixtures are returned, including res5 (["foo", "bar"]) and res6 (["group1", "bar"]), and it does not mock a user session. So the present behaviour is pinned by a test — fixing this means deciding that the test encodes a bug rather than an intent, which felt like a call for a maintainer rather than something to settle in a drive-by PR.
There is also a question I could not answer from the outside: searchPrincipals() and findByUri() both bail out when there is no session (return [] / return null), but getPrincipalsByPrefix() has no such guard, and the backend is used both by the principal collection and by CalendarRoot in RootCollection.php. If some path reaches it without a session, adding a filter there would hide every room instead of the restricted ones — a worse regression than the bug.
Happy to prepare a PR once you indicate the preferred direction (filter in the backend vs. filter in the Calendar dialog).
Nextcloud Server version
34
Operating system
Other
PHP engine version
PHP 8.4
Web server
Apache (supported)
Database engine version
PostgreSQL
Is this bug present after an update or on a fresh install?
Updated to a major version (ex. 32.0.9 to 33.0.4)
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
Are you using an external user-backend, if yes which one?
Server configuration detail
Calendar app 6.5.4. Room provided by a third-party IRoomBackend implementation; the same code path applies to any room backend.
Nextcloud Signing status
No response
Nextcloud Logs
No response
Additional info
Related: the same dialog also renders the literal string undefined inside busy blocks — that one is a Calendar-side issue and is being fixed separately in nextcloud/calendar#8780.
Bug description
group_restrictionson a calendar room or resource are enforced in some ofAbstractPrincipalBackend's read paths but not in others. As a result a restricted room stays visible to users outside the allowed group in Nextcloud Calendar's "Show rooms" dialog, while the attendee type-ahead correctly hides it.isAllowedToAccessResource()is called fromsearchPrincipals()andfindByUri(), but not fromgetPrincipalsByPrefix()orgetPrincipalByPath()— those two do not even select thegroup_restrictionscolumn:group_restrictionsisAllowedToAccessResource()getPrincipalsByPrefix()(L70)getPrincipalByPath()(L121)searchPrincipals()(L204)findByUri()(L400)Calendar fills the "Show rooms" dialog via
fetchRoomAndResourcePrincipals(), which does aPROPFINDondav/principals/calendar-rooms/— i.e. straight throughgetPrincipalsByPrefix().Only the room's name, email and metadata leak; booking is still rejected server-side. But room names and locations can reveal organisational structure, which is exactly what the restriction is meant to prevent.
Steps to reproduce
IRoomBackend).group_restrictionsvalue, e.g.["test"], and let the hourlyUpdateCalendarResourcesRoomsBackgroundJobwrite it tooc_calendar_rooms.Expected behaviour
The restricted room is not listed, consistent with the attendee type-ahead.
Actual behaviour
The room is listed with its name, email and metadata.
Measured directly against the backend on a live NC 34 instance, with two test accounts differing only in group membership:
Setting the restriction back to
[]makes both users see the room again through both paths, which confirms the restriction itself is stored and evaluated correctly — one of the two read paths simply skips the check.Note for whoever picks this up
AbstractPrincipalBackendTestCase::testGetPrincipalsByPrefix()currently asserts that all six fixtures are returned, includingres5(["foo", "bar"]) andres6(["group1", "bar"]), and it does not mock a user session. So the present behaviour is pinned by a test — fixing this means deciding that the test encodes a bug rather than an intent, which felt like a call for a maintainer rather than something to settle in a drive-by PR.There is also a question I could not answer from the outside:
searchPrincipals()andfindByUri()both bail out when there is no session (return []/return null), butgetPrincipalsByPrefix()has no such guard, and the backend is used both by the principal collection and byCalendarRootinRootCollection.php. If some path reaches it without a session, adding a filter there would hide every room instead of the restricted ones — a worse regression than the bug.Happy to prepare a PR once you indicate the preferred direction (filter in the backend vs. filter in the Calendar dialog).
Nextcloud Server version
34
Operating system
Other
PHP engine version
PHP 8.4
Web server
Apache (supported)
Database engine version
PostgreSQL
Is this bug present after an update or on a fresh install?
Updated to a major version (ex. 32.0.9 to 33.0.4)
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
Are you using an external user-backend, if yes which one?
Server configuration detail
Calendar app 6.5.4. Room provided by a third-party
IRoomBackendimplementation; the same code path applies to any room backend.Nextcloud Signing status
No response
Nextcloud Logs
No response
Additional info
Related: the same dialog also renders the literal string
undefinedinside busy blocks — that one is a Calendar-side issue and is being fixed separately in nextcloud/calendar#8780.