Skip to content

ATS-PFM-SEC-020Match portal memberships to CRM contacts: Capability Test Specification

Markdown

ATS-PFM-SEC-020

Match portal memberships to CRM contacts: Capability Test Specification

Markdown
  • DocumentProposed

KindAcceptance test setProduct areaPlatformModuleSecurity, Customers and WorkspacesRevision3ExaminercodexExamined on2026-10-23

Read atservices-platform ad3f6a65

Coverage

  • Rule covered
  • Transition covered
  • Validation covered
  • Permission gap: FND-PFM-SEC-077
  • Edge gap: FND-PFM-SEC-077
  • Failure gap: FND-PFM-SEC-077
  • Boundary covered
  • Concurrency gap: FND-PFM-SEC-077
  • Invariant covered
  • Interaction covered

Test Scenarios

13 scenarios proving CAP-PFM-SEC-020; 8 bound to an existing test.

01 Link an exact address match or create a contactruleapitest
Proves
BR-CAP-PFM-SEC-020-01
Preconditions
Prepare one eligible matching address and a member with no likely CRM match.
Steps
Evaluate matching for both members.
Expected
Link the exact match directly; create and link a contact for the unmatched member.
Data
Use the records described in the setup.
Bound to
services-platform:modules/security/tests/integration/services/portal/contact-match.integration.spec.js#links the one contact holding the member’s own address without asking
services-platform:modules/security/tests/integration/services/portal/contact-match.integration.spec.js#creates and links a new contact when CRM finds nobody likely, recording no declined pair
services-platform:modules/security/tests/unit/services/portal/contact-match-rules.spec.ts#links without asking the one contact holding the member’s own address, compared case-insensitively
Source
services-platform:modules/security/tests/integration/services/portal/contact-match.integration.spec.js#links the one contact holding the member’s own address without asking
02 Exclude contacts linked by another memberinvariantapinegativetest
Proves
BR-CAP-PFM-SEC-020-02
Preconditions
Prepare a candidate already linked in the workspace.
Steps
Evaluate candidate selection.
Expected
Neither offer nor link that contact.
Data
Use the records described in the setup.
Bound to
services-platform:modules/security/tests/unit/services/portal/contact-match-rules.spec.ts#never offers a contact a portal member already links, and links nothing through one
Source
services-platform:modules/security/tests/integration/services/portal/contact-match.integration.spec.js#never offers a contact another member of the tenant already links
03 Return a limited masked candidate listboundaryunitnegativetest
Proves
BR-CAP-PFM-SEC-020-03
Preconditions
Prepare five scored candidate contacts.
Steps
Request the candidate response.
Expected
Return the highest three distinct candidates with masked hints and no contact identifiers.
Data
Use the records described in the setup.
Bound to
services-platform:modules/security/tests/unit/services/portal/contact-match-rules.spec.ts#offers at most three, highest score first, each contact once
services-platform:modules/security/tests/unit/services/portal/contact-match-rules.spec.ts#gives each candidate its masked hints and nothing else of the contact
services-platform:modules/security/tests/unit/services/portal/contact-match-rules.spec.ts#masks an e-mail address to its first letter, its domain’s first letter and its top-level domain
Source
services-platform:modules/security/tests/unit/services/portal/contact-match-rules.spec.ts#offers at most three, highest score first, each contact once
04 Bind candidate tokens to their intended recipientvalidationunitnegativetest
Proves
BR-CAP-PFM-SEC-020-04
Preconditions
Prepare a sealed candidate token.
Steps
Open a modified or expired token and reuse the original with another member or workspace.
Expected
Reject all four attempts.
Data
Use the records described in the setup.
Bound to
Source
services-platform:modules/security/tests/unit/services/portal/candidate-token.spec.ts#refuses a token with one character changed
05 Bound verification challengesboundaryapinegativetest
Proves
BR-CAP-PFM-SEC-020-05 INV-DOM-PFM-SEC-003-22
Preconditions
Prepare an eligible member and candidate.
Steps
Start six challenges within an hour, submit five incorrect codes and submit a correct code after expiry.
Expected
Reject the sixth start, exhaust the fifth failed attempt and refuse expired proof. Store a digest without the code.
Data
Use the records described in the setup.
Bound to
services-platform:modules/security/tests/integration/services/persistence/portal-contact-challenges.integration.spec.js#records the member, the contact, the channel and a ten-minute expiry, with the digest of the code and never the code
services-platform:modules/security/tests/integration/services/persistence/portal-contact-challenges.integration.spec.js#supersedes the member’s live challenge, so an old code can never be entered after a new one was sent
Source
services-platform:modules/security/tests/integration/services/persistence/portal-contact-challenges.integration.spec.js#records the member, the contact, the channel and a ten-minute expiry, with the digest of the code and never the code
06 Establish one link from valid prooftransitionapinegativetest
Proves
BR-CAP-PFM-SEC-020-06 INV-DOM-PFM-SEC-003-22
Preconditions
Prepare a live challenge.
Steps
Submit an incorrect code, then the correct code, then request another challenge.
Expected
Report remaining attempts, establish the link and reject further challenge creation.
Data
Use the records described in the setup.
Bound to
Source
services-platform:modules/security/tests/integration/services/portal/contact-match.integration.spec.js#refuses a wrong code, saying how many attempts are left
07 Retain a possible duplicate after decliningtransitionapinegativetest
Proves
BR-CAP-PFM-SEC-020-07
Preconditions
Prepare a member with ranked candidates.
Steps
Decline every candidate.
Expected
Create and link a new contact while recording the highest-ranked declined contact for review.
Data
Use the records described in the setup.
Bound to
Source
services-platform:modules/security/tests/integration/services/portal/contact-match.integration.spec.js#is linked to a contact CRM created from the sign-up details
08 Refresh the contact claim on authorizer evaluationinteractionapinegativetest
Proves
BR-CAP-PFM-SEC-020-08
Preconditions
Prepare portal and console callers.
Steps
Evaluate the portal caller, change and clear its contact link and evaluate again without gateway cache reuse; also evaluate the console caller.
Expected
Fresh portal contexts carry the current link or omit a cleared link. Console contexts omit the contact claim. The binding does not establish gateway propagation timing.
Data
Use the records described in the setup.
Bound to
services-platform:modules/security/tests/integration/services/authorizer-portal-contact.integration.spec.js#carries the linked contact as `cid`, read from the user’s row under the deployed role
Source
services-platform:modules/security/tests/integration/services/authorizer-portal-contact.integration.spec.js#carries the linked contact as `cid`, read from the user’s row under the deployed role
09 Portal contact challenge: live to verifiedtransitionapiproposed
Proves
BR-CAP-PFM-SEC-020-06
Preconditions
Prepare a run-owned portal contact challenge in live state with the authority and inputs required by the cited transition.
Steps
[ { "do": "Submit the code delivered for this member’s selected contact and read the link.", "expect": "After the stated action, observe the portal contact challenge in verified state. This case does not require an additional history event unless the transition explicitly defines one.", "provenance": "proposed", "source": [ "services-platform:modules/security/services/portal/contact-match.ts:verifyContactChallenge", "services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts." ] } ]
Expected
After the stated action, observe the portal contact challenge in verified state. This case does not require an additional history event unless the transition explicitly defines one.
Data
A disposable customer, workspace and named actor; a controllable clock where the transition depends on time.
Source
services-platform:modules/security/services/portal/contact-match.ts:verifyContactChallenge,services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts.
10 Portal contact challenge: live to exhaustedtransitionapiproposed
Proves
BR-CAP-PFM-SEC-020-05
Preconditions
Prepare a run-owned portal contact challenge in live state with the authority and inputs required by the cited transition.
Steps
[ { "do": "Submit five incorrect codes and try the correct code after the fifth failure.", "expect": "After the stated action, observe the portal contact challenge in exhausted state. This case does not require an additional history event unless the transition explicitly defines one.", "provenance": "proposed", "source": [ "services-platform:modules/security/db/portal-contact-challenges.table.ts:PORTAL_CHALLENGE_MAX_ATTEMPTS", "services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts." ] } ]
Expected
After the stated action, observe the portal contact challenge in exhausted state. This case does not require an additional history event unless the transition explicitly defines one.
Data
A disposable customer, workspace and named actor; a controllable clock where the transition depends on time.
Source
services-platform:modules/security/db/portal-contact-challenges.table.ts:PORTAL_CHALLENGE_MAX_ATTEMPTS,services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts.
11 Portal contact challenge: live to expiredtransitionapiproposed
Proves
BR-CAP-PFM-SEC-020-05
Preconditions
Prepare a run-owned portal contact challenge in live state with the authority and inputs required by the cited transition.
Steps
[ { "do": "Advance ten minutes from challenge creation and submit the previously correct code.", "expect": "After the stated action, observe the portal contact challenge in expired state. This case does not require an additional history event unless the transition explicitly defines one.", "provenance": "proposed", "source": [ "services-platform:modules/security/db/portal-contact-challenges.table.ts:PORTAL_CHALLENGE_OUTCOMES", "services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts." ] } ]
Expected
After the stated action, observe the portal contact challenge in expired state. This case does not require an additional history event unless the transition explicitly defines one.
Data
A disposable customer, workspace and named actor; a controllable clock where the transition depends on time.
Source
services-platform:modules/security/db/portal-contact-challenges.table.ts:PORTAL_CHALLENGE_OUTCOMES,services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts.
12 Portal contact challenge: live to supersededtransitionapiproposed
Proves
BR-CAP-PFM-SEC-020-05
Preconditions
Prepare a run-owned portal contact challenge in live state with the authority and inputs required by the cited transition.
Steps
[ { "do": "Start a replacement challenge and try verifying the earlier challenge.", "expect": "After the stated action, observe the portal contact challenge in superseded state. This case does not require an additional history event unless the transition explicitly defines one.", "provenance": "proposed", "source": [ "services-platform:modules/security/services/portal/contact-match.ts:startContactChallenge", "services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts." ] } ]
Expected
After the stated action, observe the portal contact challenge in superseded state. This case does not require an additional history event unless the transition explicitly defines one.
Data
A disposable customer, workspace and named actor; a controllable clock where the transition depends on time.
Source
services-platform:modules/security/services/portal/contact-match.ts:startContactChallenge,services-platform at ad3f6a65: modules/security/db/portal-contact-challenges.table.ts; services/portal/contact-match.ts, contact-match-rules.ts, verification-code-sender.ts.
13 Match portal memberships to CRM contacts with derive member access from appointments and eligibilityinteractionapiproposed
Proves
BR-CAP-PFM-SEC-014-01 BR-CAP-PFM-SEC-020-08
Preconditions
An unlinked portal member has a contact carrying derived authority.
Steps
Complete contact possession proof and request a fresh authorization context.
Expected
The linked contact claim enables its applicable derived assignments; a console context carries no contact claim.
Data
An isolated customer with two workspaces, ordinary and staff identities, and records owned by this run.
Source
services-platform:modules/security/tests/e2e/derived-assignments.e2e.spec.js#is refused once the member is no longer linked to the contact, so the link is what carries the grant,services-platform:docs/security/portal-contact-claim.md#When a change is seen

The Markdown files under specs/ in rhythmsoftware/rhythm-v2-docs are the canonical source of truth. This reader is a generated, read-only projection of them.