Skip to content

fix: await promises in VisitAuthorization hasReadRights - #1754

Open
jekabs-karklins wants to merge 1 commit into
developfrom
SWAP-5726-await-promises-in-visitauthorization-hasreadrights
Open

fix: await promises in VisitAuthorization hasReadRights#1754
jekabs-karklins wants to merge 1 commit into
developfrom
SWAP-5726-await-promises-in-visitauthorization-hasreadrights

Conversation

@jekabs-karklins

@jekabs-karklins jekabs-karklins commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The two async checks in VisitAuthorization.hasReadRights were never awaited which caused issue that visitor could not load the questionaire.

return (
  visit.creatorId === agent.id ||                                  // sync
  this.proposalAuth.isMemberOfProposal(agent, visit.proposalPk) || // async, not awaited
  this.visitDataSource.isVisitorOfVisit(agent.id, visit.id)        // async, not awaited
);
image

The first line is sync, and the last two is async.

one is false → || moves on
two is a pending Promise → truthy → || short-circuits and returns two itself
three is never evaluated

The method was silently equivalent to:

return visit.creatorId === agent.id || (await this.proposalAuth.isMemberOfProposal(...));

The visitor-list check was dead code for everyone. Proposal members passed on the second check, so this only surfaced for a user on the visitor list who is not a proposal member — they saw Failed to load questionary details when opening the visit registration.

How Has This Been Tested

Manually, as the reporting visitor: the visit registration questionary now loads.

Fixes

SWAP-5726

Changes

apps/backend/src/auth/VisitAuthorization.ts — await both async checks.

The two async checks in hasReadRights were not awaited. Because an
unawaited promise is truthy, `||` short-circuited on isMemberOfProposal
and returned that promise object, so isVisitorOfVisit was never
evaluated. The async wrapper then adopted the returned promise, making
the method silently equivalent to:

  visit.creatorId === agent.id || (await isMemberOfProposal(...))

The visitor-list check was therefore dead code. Proposal members passed
on the second check, so the bug only surfaced for a user who is on the
visitor list without being a proposal member -- they got "Failed to load
questionary details" when opening the visit registration.

SWAP-5726

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jekabs-karklins
jekabs-karklins requested a review from a team as a code owner August 26, 2026 12:47
@jekabs-karklins
jekabs-karklins requested review from DChambersSTFC and yoganandaness and removed request for a team August 26, 2026 12:47
visit.creatorId === agent.id ||
this.proposalAuth.isMemberOfProposal(agent, visit.proposalPk) ||
this.visitDataSource.isVisitorOfVisit(agent.id, visit.id)
(await this.proposalAuth.isMemberOfProposal(agent, visit.proposalPk)) ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some parallelization?

const [isMember, isVisitor] = await Promise.all([
this.proposalAuth.isMemberOfProposal(agent, visit.proposalPk),
this.visitDataSource.isVisitorOfVisit(agent.id, visit.id),
]);

return visit.creatorId === agent.id || isMember || isVisitor;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants