Skip to content

implement workshop side of gatekeeper sharing api - #479

Draft
maxwellpeterson wants to merge 1 commit into
mpeterson/user-directoryfrom
mpeterson/gatekeeper-sharing-kernel
Draft

implement workshop side of gatekeeper sharing api#479
maxwellpeterson wants to merge 1 commit into
mpeterson/user-directoryfrom
mpeterson/gatekeeper-sharing-kernel

Conversation

@maxwellpeterson

Copy link
Copy Markdown
Member

WIP

User eligibility isn't checked until the user is selected from search results, so users that are ineligible to receive the share will still appear in search results and fail with a helpful error message when selected. This lets us re-use the same user search API used for workspace sharing. If we wanted to scope search results to just eligible users, the user directory would need to become aware of each user's connected accounts and connection state, which I'm not sure we want.

@github-actions github-actions Bot added workshop/frontend Changes to the Workshop frontend kernel Changes to the Workshop kernel workshop/shared Changes to shared Workshop APIs labels Sep 11, 2026
@github-actions

Copy link
Copy Markdown

Preview: pr479-mpeterson-gat-dfce2258

https://pr479-mpeterson-gat-dfce2258-router.cloudflare-os-previews.workers.dev

Dashboard · deleted when this PR closes

if (userId === this.#userId.name) return null;
let verifier = await this.users.getByName(userId).getUniqueGatekeeperUserVerifier(gatekeeperId);
if (!verifier) return null;
return { verifier, profile: this.ctx.exports.GatekeeperUserProfileImpl({ props: { userId } }) };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Returning this raw verifier from AuthenticatedApi hands it to arbitrary browser code, not only to the gatekeeper that minted it. Cap’n Web stubs expose every prototype method at runtime regardless of the empty GatekeeperUserVerifier interface, so an authenticated user can call this with e.g. gatekeeperId = "google", cast the returned verifier, and invoke hasDocAccess() / verifyDriveFiles() using the target user’s credentials. The caller-controlled vendor is not even tied to an app returned by getGatekeeperApp(). This breaks the existing same-vendor handoff invariant and creates a cross-user ACL/provider-request oracle. Please bind selection to the exact opened management-app capability and arrange for the verifier to be consumed server-to-server rather than returning it over the browser RPC.

(value: string) => authenticatedApi.searchUsers(value, pickedIds), [authenticatedApi, pickedIds])

const pick = (user: UserDirectoryRecord) => {
authenticatedApi.selectGatekeeperUser(gatekeeperId, user.id).then(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This request can settle after Cancel/unmount, or after Done has transferred the currently resolved picks. In either case a successful late response reaches setPicks() after the dialog is gone and neither returned stub is disposed, leaking both remote capabilities for the lifetime of the WebSocket session. The same missing in-flight tracking permits double-click duplicates and out-of-order/omitted selections. Track requests against the picker session, reserve each user/order when the request starts, prevent completion while requests are pending, and dispose any result that arrives after completion or unmount.

return (
<Dialog.Root open onOpenChange={(open) => { if (!open) cancel() }}>
<Dialog
className="responsive-dialog !z-[2147483100] !w-[min(520px,calc(100vw-32px))] bg-kumo-base p-0 !outline-none"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This raises only Kumo’s popup. Kumo renders its backdrop as a separate fixed portal sibling with no z-index, while this app iframe can already be at 2147483000 after setPresenting(true). A gatekeeper can therefore open the picker while presented and remain visible and pointer-interactive above the backdrop everywhere outside the trusted popup; iframe clicks do not bubble to the host dialog. Please put the whole dialog portal/backdrop above the iframe or make the iframe inert/non-interactive while the trusted picker is open.

@ask-bonk

ask-bonk Bot commented Sep 11, 2026

Copy link
Copy Markdown

Posted 3 actionable inline findings. CI build, tests, and lint are passing.

github run

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

Labels

kernel Changes to the Workshop kernel workshop/frontend Changes to the Workshop frontend workshop/shared Changes to shared Workshop APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant