import fetchHelper from "../../../utils/fetchHelper" import refreshTokenHelper from "../../../utils/refreshTokenHelper" import errorHandlingHelper from "../../../utils/errorHandlingHelper" // Ownership guard for limited roles (FrontendMenu === 'c'). // // The write below uses the SuperUser service token because c_location is a // SHARED table (org 0) that a limited role's own token cannot write. To make // sure a limited user can only rewrite addresses they actually own — not an // arbitrary c_location id — we first confirm the caller can SEE a // c_bpartner_location that references this c_location, using the caller's OWN // org-scoped token. iDempiere only returns records in orgs the role can access, // so a visible reference proves legitimate ownership. Fail-closed on any error. const callerOwnsLocation = async (event: any, locationId: any) => { const url = `models/c_bpartner_location?$filter=C_Location_ID eq ${locationId}&$select=C_Location_ID,AD_Org_ID&$top=1` const check = async (token: any) => { const res: any = await fetchHelper(event, url, 'GET', token, null) return (res?.records?.length ?? 0) > 0 } try { return await check(await getTokenHelper(event)) } catch { try { return await check(await refreshTokenHelper(event)) } catch { return false } } } const handleFunc = async (event: any) => { let data: any = {} const config = useRuntimeConfig() const serviceToken = config.api.idempieretoken const body = await readBody(event) if (!serviceToken) { return { status: 500, message: 'Service token not configured' } } if (!body.id) { return { status: 400, message: 'Missing location id' } } // Only limited roles are gated; staff roles keep unrestricted service-token access. const roleCookie = getCookie(event, 'logship_role') let menuType: string = 'Menu' try { const role = roleCookie ? JSON.parse(roleCookie) : null menuType = role?.FrontendMenu?.id || role?.FrontendMenu || 'Menu' } catch {} if (menuType === 'c' && !(await callerOwnsLocation(event, body.id))) { return { status: 403, message: 'Access denied for record with id: ' + body.id } } let requestBody: any = { address1: body.address1, address2: body.address2, city: body.city, postal: body.postal, tableName: 'C_Location' } // Add country if provided if (body.countryId) { requestBody.C_Country_ID = { id: body.countryId, tableName: 'C_Country' } } const res: any = await $fetch(`${config.api.url}/models/c_location/${body.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', Accept: 'application/json', Authorization: 'Bearer ' + serviceToken }, body: requestBody }) if(res) { data = res data['status'] = 200 data['message'] = '' } return data } export default defineEventHandler(async (event) => { let data: any = {} try { data = await handleFunc(event) } catch(err: any) { data = errorHandlingHelper(err?.data ?? err, err) } return data })