feat: scope the bulk contact update's add_campaigns and remove_campaigns SQL and its campaign readback in internal/repository/pg_contact.go by campaigns.organization_id instead of cam.user_id = caller, because campaigns are organization assets and are listed and opened org-wide, so a teammate attaching contacts to a campaign someone else created (through bulk edit or the import wizard's attach-to-campaign path) got a silent no-op with no rows inserted, matching the earlier fix that moved the contacts list itself to organization scoping while leaving categories user-scoped as that change noted

This commit is contained in:
Matthew Meszaros
2026-08-18 07:42:07 -07:00
parent 9dea6d1e22
commit 0c0fb5cefb
+7 -5
View File
@@ -1613,16 +1613,18 @@ func (r *contactRepository) BulkUpdate(ctx context.Context, userID string, orgID
*data.Subscribe, orgID, data.Contacts)
}
// Campaigns are organization assets: scoping them by the caller made a
// teammate's "add to campaign" a silent no-op on campaigns they did not create.
if len(data.RemoveCampaigns) > 0 {
b.Queue(`DELETE FROM campaign_leads cl
USING contacts c, campaigns cam
WHERE cl.contact_id = c.id
AND cl.campaign_id = cam.id
AND c.organization_id = $1
AND cam.user_id = $4
AND cam.organization_id = $1
AND cl.contact_id = ANY($2)
AND cl.campaign_id = ANY($3)`,
orgID, data.Contacts, data.RemoveCampaigns, userID)
orgID, data.Contacts, data.RemoveCampaigns)
}
if len(data.AddCampaigns) > 0 {
@@ -1633,9 +1635,9 @@ func (r *contactRepository) BulkUpdate(ctx context.Context, userID string, orgID
WHERE c.organization_id = $1
AND c.id = ANY($2)
AND cam.id = ANY($3::uuid[])
AND cam.user_id = $4
AND cam.organization_id = $1
ON CONFLICT DO NOTHING`,
orgID, data.Contacts, data.AddCampaigns, userID)
orgID, data.Contacts, data.AddCampaigns)
}
if len(data.RemoveCategories) > 0 {
@@ -1712,7 +1714,7 @@ func (r *contactRepository) BulkUpdate(ctx context.Context, userID string, orgID
SELECT json_agg(json_build_object('id', cam.id, 'name', cam.name))
FROM campaign_leads cl
JOIN campaigns cam ON cl.campaign_id = cam.id
WHERE cl.contact_id =c.id AND cam.user_id = $2
WHERE cl.contact_id =c.id AND cam.organization_id = $3
),
'[]'::json
) AS campaigns,