fix: reject affiliations for organizations that block them - #4461
Conversation
Signed-off-by: Yeganathan S <63534555+skwowet@users.noreply.github.com>
|
Your PR title doesn't contain a Jira issue key. Consider adding it for better traceability. Example:
Projects:
Please add a Jira issue key to your PR title. |
There was a problem hiding this comment.
Pull request overview
Rejects affiliations targeting organizations that have blocked them.
Changes:
- Adds blocked-organization validation to public API updates.
- Applies equivalent validation to CDP affiliation upserts.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
memberAffiliationsService.ts |
Validates organization affiliation policies. |
patchProjectAffiliation.ts |
Returns 400 for blocked organizations. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (3)
backend/src/services/member/memberAffiliationsService.ts:76
- The policy read happens before
MemberAffiliationsRepository.upsertMultipleopens its write transaction. If this request readsfalse, then another transaction blocks the organization and deletes its affiliations before this upsert commits, this request recreates an affiliation for the now-blocked organization. Keep the policy read and insert in one transaction and lock the organization rows with a mode that conflicts with policy updates (for exampleFOR SHARE), or enforce this invariant in the database.
const qx = SequelizeRepository.getQueryExecutor(this.options)
const organizationIds = data
.map((a) => a.organizationId)
.filter((id): id is string => Boolean(id))
const policies = await fetchManyOrganizationAffiliationPolicies(qx, organizationIds)
backend/src/api/public/v1/members/project-affiliations/patchProjectAffiliation.ts:70
- This check is outside the transaction that replaces the affiliations. A concurrent organization-block operation can update the policy and finish deleting affiliations after this read but before this transaction inserts, leaving a new affiliation on a blocked organization. Perform the policy read inside the write transaction while locking the organization rows with a mode that conflicts with policy updates (for example
FOR SHARE), or enforce the invariant in the database.
const policies = await fetchManyOrganizationAffiliationPolicies(
qx,
affiliations.map((a) => a.organizationId),
)
backend/src/services/member/memberAffiliationsService.ts:79
Error400treats its second argument as an i18n key; in production, an unregistered literal falls back to the generic validation message, so CDP users will not receive the newly intended explanation. UseBadRequestError('This organization does not allow affiliations')(and import it) or add and pass a real translation key.
throw new Error400(this.options.language, 'This organization does not allow affiliations')
Summary
Rejects creating member segment affiliations that target an organization with
isAffiliationBlocked, closing the gap where the public API and CDP UI could reintroduce blocked-org activity affiliations after an org was already blocked.Changes
PATCH /members/:id/project-affiliations/:projectIdreturns 400 when any affiliation org has affiliations blockedPUT /member/:id/affiliationapplies the same check before upserting MSAs