// Group membership model: the single source of truth for the OIDC `groups` claim // (and, in a later phase, LDAP groups). A user's effective groups = their // Saltcorn role exposed as a group (role:) UNION their custom group // memberships (group:), so a role and a custom group with the same name // never collide. const db = require("@saltcorn/data/db"); const { TABLE_GROUPS, TABLE_GROUP_MEMBERS } = require("./constants"); const ROLE_PREFIX = "role:"; const GROUP_PREFIX = "group:"; const listGroups = async () => { return await db.select(TABLE_GROUPS, {}, { orderBy: "name" }); }; const createGroup = async (name, description) => { return await db.insert(TABLE_GROUPS, { name: name, description: description || null, created_at: new Date().toISOString() }); }; const deleteGroup = async (id) => { await db.deleteWhere(TABLE_GROUP_MEMBERS, { group_id: id }); await db.deleteWhere(TABLE_GROUPS, { id: id }); }; const membersOf = async (groupId) => { return await db.select(TABLE_GROUP_MEMBERS, { group_id: groupId }); }; const addMember = async (groupId, userId) => { const existing = await db.selectMaybeOne(TABLE_GROUP_MEMBERS, { group_id: groupId, user_id: userId }); if (existing) { return; } await db.insert(TABLE_GROUP_MEMBERS, { group_id: groupId, user_id: userId }, { noid: true }); }; const removeMember = async (groupId, userId) => { await db.deleteWhere(TABLE_GROUP_MEMBERS, { group_id: groupId, user_id: userId }); }; const effectiveGroups = async (user) => { const out = []; const role = await db.selectMaybeOne("_sc_roles", { id: user.role_id }); if (role && role.role) { out.push(ROLE_PREFIX + role.role); } const members = await db.select(TABLE_GROUP_MEMBERS, { user_id: user.id }); for (const member of members) { const group = await db.selectMaybeOne(TABLE_GROUPS, { id: member.group_id }); if (group && group.name) { out.push(GROUP_PREFIX + group.name); } } return out; }; module.exports = { listGroups, createGroup, deleteGroup, membersOf, addMember, removeMember, effectiveGroups };