security: A01-13 — nest all event routes under organisation prefix
Move all authenticated organiser-facing event sub-resource routes from
/events/{event}/... to /organisations/{organisation}/events/{event}/...
to enforce multi-tenancy at the routing layer.
Changes:
- Routes: restructured api.php to nest all event sub-resources under
the existing organisation prefix group
- Controllers: added Organisation parameter and VerifiesOrganisationEvent
trait to all 12 affected controllers (sections, time-slots, shifts,
persons, crowd-lists, locations, shift-assignments, registration-fields,
availabilities, field-values, section-preferences, stats)
- Tests: updated all 20 feature test files with new route paths
- Frontend: updated 8 API composables and 20 Vue components/pages
- API.md: updated documentation to reflect new route structure
Portal routes, public routes (volunteer-register), and invitation routes
remain unchanged as they operate without organisation context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,10 +21,10 @@ const authStore = useAuthStore()
|
||||
const orgId = computed(() => authStore.currentOrganisation?.id ?? '')
|
||||
const eventId = computed(() => String((route.params as { id: string }).id))
|
||||
|
||||
const { data: crowdLists, isLoading, isError, refetch } = useCrowdLists(eventId)
|
||||
const { data: crowdLists, isLoading, isError, refetch } = useCrowdLists(orgId, eventId)
|
||||
const { data: crowdTypes } = useCrowdTypeList(orgId)
|
||||
const { data: companies } = useCompanies(orgId)
|
||||
const { mutate: deleteCrowdList, isPending: isDeleting } = useDeleteCrowdList(eventId)
|
||||
const { mutate: deleteCrowdList, isPending: isDeleting } = useDeleteCrowdList(orgId, eventId)
|
||||
|
||||
// Lookup maps for resolving IDs to names
|
||||
const crowdTypeMap = computed(() => {
|
||||
|
||||
@@ -29,7 +29,7 @@ const filters = computed(() => ({
|
||||
status: filterStatus.value || undefined,
|
||||
}))
|
||||
|
||||
const { data: personsResponse, isLoading, isError, refetch } = usePersonList(eventId, filters)
|
||||
const { data: personsResponse, isLoading, isError, refetch } = usePersonList(orgId, eventId, filters)
|
||||
const { data: crowdTypes } = useCrowdTypeList(orgId)
|
||||
|
||||
const persons = computed(() => personsResponse.value?.data ?? [])
|
||||
@@ -123,8 +123,8 @@ const isDeleteDialogOpen = ref(false)
|
||||
const deletingPerson = ref<Person | null>(null)
|
||||
|
||||
// Mutations
|
||||
const { mutate: approvePerson } = useApprovePerson(eventId)
|
||||
const { mutate: deletePerson, isPending: isDeleting } = useDeletePerson(eventId)
|
||||
const { mutate: approvePerson } = useApprovePerson(orgId, eventId)
|
||||
const { mutate: deletePerson, isPending: isDeleting } = useDeletePerson(orgId, eventId)
|
||||
|
||||
const showSuccess = ref(false)
|
||||
const successMessage = ref('')
|
||||
|
||||
@@ -35,11 +35,11 @@ const eventId = computed(() => String((route.params as { id: string }).id))
|
||||
const { mutate: updateEvent, isPending: isUpdatingEvent } = useUpdateEvent(orgId, eventId)
|
||||
|
||||
// Registration form fields
|
||||
const { data: fields, isLoading } = useRegistrationFormFields(eventId)
|
||||
const { mutate: createField, isPending: isCreating } = useCreateRegistrationFormField(eventId)
|
||||
const { mutate: updateField, isPending: isUpdating } = useUpdateRegistrationFormField(eventId)
|
||||
const { mutate: deleteField, isPending: isDeleting } = useDeleteRegistrationFormField(eventId)
|
||||
const { mutate: reorderFields } = useReorderRegistrationFormFields(eventId)
|
||||
const { data: fields, isLoading } = useRegistrationFormFields(orgId, eventId)
|
||||
const { mutate: createField, isPending: isCreating } = useCreateRegistrationFormField(orgId, eventId)
|
||||
const { mutate: updateField, isPending: isUpdating } = useUpdateRegistrationFormField(orgId, eventId)
|
||||
const { mutate: deleteField, isPending: isDeleting } = useDeleteRegistrationFormField(orgId, eventId)
|
||||
const { mutate: reorderFields } = useReorderRegistrationFormFields(orgId, eventId)
|
||||
|
||||
// Local draggable list synced from query
|
||||
const localFields = ref<RegistrationFormField[]>([])
|
||||
|
||||
@@ -20,8 +20,8 @@ const authStore = useAuthStore()
|
||||
const orgId = computed(() => authStore.currentOrganisation?.id ?? '')
|
||||
const eventId = computed(() => String((route.params as { id: string }).id))
|
||||
|
||||
const { data: timeSlots, isLoading, isError, refetch } = useTimeSlotList(eventId)
|
||||
const { mutate: deleteTimeSlotMutation, isPending: isDeletingTimeSlot } = useDeleteTimeSlot(eventId)
|
||||
const { data: timeSlots, isLoading, isError, refetch } = useTimeSlotList(orgId, eventId)
|
||||
const { mutate: deleteTimeSlotMutation, isPending: isDeletingTimeSlot } = useDeleteTimeSlot(orgId, eventId)
|
||||
|
||||
// Load children for festivals — needed for time slot context chips
|
||||
const { data: children } = useEventChildren(orgId, eventId)
|
||||
|
||||
Reference in New Issue
Block a user