Adds initial models, migrations, and services to support role-based access and sharing; wires routes to prepare for permission-driven features.
67 lines
2.4 KiB
JavaScript
67 lines
2.4 KiB
JavaScript
const { sequelize, Action } = require('../models');
|
|
const { isAdmin } = require('./rolesService');
|
|
const { applyPerms } = require('./applyPerms');
|
|
const { calculateProjectPerms, calculateTaskPerms, calculateNotePerms, calculateAreaPerms, calculateTagPerms } = require('./permissionsCalculators');
|
|
|
|
async function assertActorCanShare(actorUserId, resourceType, resourceOwnerId) {
|
|
if (await isAdmin(actorUserId)) return;
|
|
if (resourceOwnerId !== actorUserId) {
|
|
const err = new Error('Forbidden');
|
|
err.status = 403;
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
async function execAction(action) {
|
|
// action: { verb, actorUserId, targetUserId, resourceType, resourceUid, accessLevel? }
|
|
return await sequelize.transaction(async (tx) => {
|
|
// Resolve owner id for authorization (basic impl for projects; extend later)
|
|
let ownerUserId = null;
|
|
if (action.resourceType === 'project') {
|
|
const { Project } = require('../models');
|
|
const proj = await Project.findOne({ where: { uid: action.resourceUid }, attributes: ['user_id'], transaction: tx, lock: tx.LOCK.UPDATE });
|
|
if (!proj) {
|
|
const err = new Error('Resource not found');
|
|
err.status = 404;
|
|
throw err;
|
|
}
|
|
ownerUserId = proj.user_id;
|
|
}
|
|
|
|
await assertActorCanShare(action.actorUserId, action.resourceType, ownerUserId);
|
|
|
|
const actionRow = await Action.create({
|
|
actor_user_id: action.actorUserId,
|
|
verb: action.verb,
|
|
resource_type: action.resourceType,
|
|
resource_uid: action.resourceUid,
|
|
target_user_id: action.targetUserId,
|
|
access_level: action.accessLevel || null,
|
|
metadata: null,
|
|
}, { transaction: tx });
|
|
|
|
let changes = { upserts: [], deletes: [] };
|
|
const ctx = { tx };
|
|
|
|
if (action.resourceType === 'project') {
|
|
changes = await calculateProjectPerms(ctx, action);
|
|
} else if (action.resourceType === 'task') {
|
|
changes = await calculateTaskPerms(ctx, action);
|
|
} else if (action.resourceType === 'note') {
|
|
changes = await calculateNotePerms(ctx, action);
|
|
} else if (action.resourceType === 'area') {
|
|
changes = await calculateAreaPerms(ctx, action);
|
|
} else if (action.resourceType === 'tag') {
|
|
changes = await calculateTagPerms(ctx, action);
|
|
}
|
|
|
|
// Attach source_action_id
|
|
changes.upserts = changes.upserts.map((u) => ({ ...u, source_action_id: actionRow.id }));
|
|
|
|
await applyPerms(tx, changes);
|
|
|
|
return actionRow.id;
|
|
});
|
|
}
|
|
|
|
module.exports = { execAction };
|