- Ya
- 10 Agosti 2026, 18:04 UTC
- Mwandishi
- Kamo
- Ahadi ya
- 987756a
GET /api/calendar/calendars took userId as a request parameter, so changing a number in the URL read anyone's calendars — and because calendars carried no organisation at all, across every tenant. Worse, no mutation checked anything: POST bound a whole UserCalendar so the caller chose its owner, and PUT and DELETE on both calendars and events resolved by id with no ownership test whatsoever. Any member could edit or delete anyone's. Organisation and member now both come from the established session. MemberCalendarService holds the member-scoped path. CalendarService stays the entity-level facade CalDAV and external sync share, because those authenticate an account rather than a membership — a genuinely different scope, not an oversight. The organisation calendar is readable by every member and writable only with CREATE_ORG_CALENDAR_EVENTS. A refusal is 403, not 404: the member can see the calendar, so pretending it does not exist would be a lie the client cannot act on. A client also cannot promote its own calendar by sending isOrgCalendar — that flag is ignored on create. Payloads become DTOs. The entities' member, organisation and user associations are lazy, so Jackson rendered them as null and a client could not tell whose calendar it held. The event DTO carries isPrivate and isRecurring even though nothing here varies on them: the modal binds both to switches, and a missing value turns a controlled input uncontrolled. listEvents also accepts startDate/endDate. The browser has always sent those against a signature that bound only start/end, so every request arrived with no window at all.