@fonderie/workspaces 6.0.1 → 6.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1217,6 +1217,18 @@ async function cancelInvitation(invitationId, workspaceId, store) {
1217
1217
  [invitationId, workspaceId]
1218
1218
  );
1219
1219
  }
1220
+ async function assertRoleAssignable(roleId, workspaceId, store) {
1221
+ const rows = await store.query(
1222
+ `SELECT id FROM fonderie_roles
1223
+ WHERE id = $1
1224
+ AND (
1225
+ (workspace_id = $2 AND is_system = false)
1226
+ OR (is_system = true AND name = 'GUEST')
1227
+ )`,
1228
+ [roleId, workspaceId]
1229
+ );
1230
+ if (rows.length === 0) throw new Error("Invitation role is no longer assignable");
1231
+ }
1220
1232
  async function acceptInvitationByPin(opts, store) {
1221
1233
  const [inv] = await store.query(
1222
1234
  `SELECT id, workspace_id AS "workspaceId", role_id AS "roleId", expires_at AS "expiresAt"
@@ -1226,6 +1238,7 @@ async function acceptInvitationByPin(opts, store) {
1226
1238
  );
1227
1239
  if (!inv) throw new Error("Invalid PIN");
1228
1240
  if (/* @__PURE__ */ new Date() > new Date(inv.expiresAt)) throw new Error("Invitation expired");
1241
+ await assertRoleAssignable(inv.roleId, inv.workspaceId, store);
1229
1242
  await store.transaction(async (tx) => {
1230
1243
  await Promise.all([
1231
1244
  tx.query(
@@ -1251,6 +1264,7 @@ async function acceptInvitationByToken(token, userId, store) {
1251
1264
  );
1252
1265
  if (!inv) throw new Error("Invalid token");
1253
1266
  if (/* @__PURE__ */ new Date() > new Date(inv.expiresAt)) throw new Error("Invitation expired");
1267
+ await assertRoleAssignable(inv.roleId, inv.workspaceId, store);
1254
1268
  await store.transaction(async (tx) => {
1255
1269
  await Promise.all([
1256
1270
  tx.query(
@@ -1358,6 +1372,25 @@ function invitationController(store, ttl, bus) {
1358
1372
  return setApiResponse6(HTTP6.SERVER_ERROR, "SERVER_ERROR", "Default role not found");
1359
1373
  }
1360
1374
  }
1375
+ const explicitRoleIds = [...new Set(
1376
+ entries.map((e) => e["roleId"]).filter((r) => typeof r === "string")
1377
+ )];
1378
+ if (explicitRoleIds.length > 0) {
1379
+ const rows = await store.query(
1380
+ `SELECT id FROM fonderie_roles
1381
+ WHERE id = ANY($1) AND workspace_id = $2 AND is_system = false`,
1382
+ [explicitRoleIds, ctx.workspace.id]
1383
+ );
1384
+ const assignable = new Set(rows.map((r) => r.id));
1385
+ const bad = explicitRoleIds.find((id) => !assignable.has(id));
1386
+ if (bad) {
1387
+ return setApiResponse6(
1388
+ HTTP6.UNPROCESSABLE,
1389
+ "INVALID_ROLE",
1390
+ "Role is not assignable in this workspace."
1391
+ );
1392
+ }
1393
+ }
1361
1394
  const results = await Promise.all(
1362
1395
  entries.map(async (entry) => {
1363
1396
  const email2 = entry["email"];