Files
wekan/server/lib/syncWebhookAccess.js
Lauri Ojansivu 2df3e9a8b7 Deliver board-level activities durably: a rule's new swimlane and cross-board moves.
Durable delivery identified every activity by a card and a list, so a
rule's createSwimlane activity, which names neither, and a cross-board
move's moveCardBoard activity, which names no list, were written once
but delivered the ordinary way: their notifications and webhooks were
not replayed after a crash. Durable delivery now takes activities
without a list, and without a card. Recipients and webhook senders are
checked against the board, plus the card when one is named. A card or
list the activity does not name is refused. An assigned-only member
never receives a board-level event, since its scope cannot be checked
against their assignments. The rule stage has nothing to run for these
types, because no trigger exists for them, and it refuses a list-less
activity whose type has one.

Thanks to xet7 !
2026-10-03 00:08:13 +03:00

23 lines
1.3 KiB
JavaScript

'use strict';
const { memberCan } = require('../../models/lib/boardRoleCapabilities');
const { isAssignedOnlyMember } = require('../../models/lib/boardCardScope');
const { canonical, sha256 } = require('../../models/lib/changeHistoryIntegrity');
// A board-level activity (no card): the actor writes the board and is not
// restricted to their assigned cards.
function canWriteWebhookBoard({ user, board }) {
return !!(user && !user.loginDisabled && board && memberCan(board.members, user._id, 'write') &&
!isAssignedOnlyMember(board, user._id));
}
function canWriteWebhookCard({ user, board, card }) {
return !!(user && !user.loginDisabled && board && card && card.boardId === board._id &&
memberCan(board.members, user._id, 'write') &&
(!isAssignedOnlyMember(board, user._id) || (Array.isArray(card.assignees) && card.assignees.includes(user._id))));
}
function isCurrentWebhookTarget({ target, integration, activity }) {
return !!(target && integration && activity && integration.enabled === true &&
integration._id === target.integrationId && integration.boardId === target.integrationBoardId &&
[activity.boardId, '_global'].includes(integration.boardId) &&
sha256(canonical(integration)) === target.integrationHash);
}
module.exports = { canWriteWebhookCard, canWriteWebhookBoard, isCurrentWebhookTarget };