/// // // Issue #27 — TeamManager roster management + close role self-escalation. // // The collection shipped with `updateRule: '@request.auth.id = id'` and // `deleteRule: null`. That broke the TeamManager admin panel (admins could // not change roles or remove members) AND left a privilege escalation open: // the self-update rule did not restrict fields, so any authenticated user // could PATCH their own `role` to "admin". // // New rules: // update — a user may update their own record but NOT the `role` field // (onboarding only touches enrollment fields); admins may update // anyone, including `role`. // delete — admins only. // // Environments that have not applied 1781000000 yet get these rules directly // from that (updated) migration; this follow-up exists for databases where it // already ran (Labs). Applying both is harmless (same values). const UPDATE_RULE = '(@request.auth.id = id && @request.body.role:isset = false) || @request.auth.role = "admin"'; const DELETE_RULE = '@request.auth.role = "admin"'; migrate((app) => { let col; try { col = app.findCollectionByNameOrId("team_members"); } catch (_) { console.log("team_members_admin_rules: team_members does not exist — skipping."); return; } if (col.type !== "auth") { console.log("team_members_admin_rules: team_members is not an auth collection — skipping."); return; } col.updateRule = UPDATE_RULE; col.deleteRule = DELETE_RULE; app.save(col); }, (app) => { // Down: restore the previous (self-update only, superuser delete) rules. let col; try { col = app.findCollectionByNameOrId("team_members"); } catch (_) { return; } if (col.type !== "auth") { return; } col.updateRule = '@request.auth.id = id'; col.deleteRule = null; app.save(col); });