This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// The tenant wall of the effect actions (lib/effects.ts). The kernel in an action resolves the
|
||||
// tenant scope and checks the permission here, inside a query, with the same tenancy hooks over
|
||||
// ctx.db that every other procedure uses. Both are internal: only this deployment's own actions
|
||||
// call them, and they derive the actor and the organization pin from the caller's verified
|
||||
// identity, never from their arguments.
|
||||
|
||||
import { type Actor, type AnyScope, notFound, toProblem, unauthenticated } from "@conusai-kit/core"
|
||||
import { ConvexError, type Value, v } from "convex/values"
|
||||
import { internalQuery } from "./_generated/server"
|
||||
import { callContextOf, tenancyOver } from "./lib/runtime"
|
||||
|
||||
const level = v.union(v.literal("org"), v.literal("workspace"), v.literal("project"))
|
||||
|
||||
async function verifiedActor(
|
||||
auth: Parameters<typeof callContextOf>[0],
|
||||
): Promise<{ actor: Actor; pinnedOrgId?: string }> {
|
||||
const { actor, pinnedOrgId } = await callContextOf(auth)
|
||||
if (!actor) throw unauthenticated()
|
||||
return { actor, ...(pinnedOrgId === undefined ? {} : { pinnedOrgId }) }
|
||||
}
|
||||
|
||||
const problem = (error: unknown): never => {
|
||||
throw new ConvexError(toProblem(error) as unknown as Value)
|
||||
}
|
||||
|
||||
/** The scope ids of a tenant-level call, or the kernel's problem (a stranger's notFound). */
|
||||
export const resolve = internalQuery({
|
||||
args: { level, input: v.any() },
|
||||
handler: async (ctx, args) => {
|
||||
try {
|
||||
const { actor, pinnedOrgId } = await verifiedActor(ctx.auth)
|
||||
const scope = (await tenancyOver(ctx.db).resolveScope(args.level, args.input, actor, pinnedOrgId)) as AnyScope & {
|
||||
orgId: string
|
||||
workspaceId?: string
|
||||
projectId?: string
|
||||
}
|
||||
if (pinnedOrgId !== undefined && scope.orgId !== pinnedOrgId) throw notFound("no scope in this organization")
|
||||
return {
|
||||
orgId: scope.orgId,
|
||||
...(scope.workspaceId === undefined ? {} : { workspaceId: scope.workspaceId }),
|
||||
...(scope.projectId === undefined ? {} : { projectId: scope.projectId }),
|
||||
}
|
||||
} catch (error) {
|
||||
return problem(error)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
/** Whether the verified actor holds `permission` in the scope, for this procedure. */
|
||||
export const authorize = internalQuery({
|
||||
args: {
|
||||
level,
|
||||
scope: v.object({ orgId: v.string(), workspaceId: v.optional(v.string()), projectId: v.optional(v.string()) }),
|
||||
permission: v.string(),
|
||||
procedure: v.object({
|
||||
name: v.string(),
|
||||
kind: v.union(v.literal("read"), v.literal("write"), v.literal("effect")),
|
||||
}),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
try {
|
||||
const { actor, pinnedOrgId } = await verifiedActor(ctx.auth)
|
||||
if (pinnedOrgId !== undefined && args.scope.orgId !== pinnedOrgId) return false
|
||||
const scope = { level: args.level, actor, ...args.scope } as AnyScope
|
||||
return await tenancyOver(ctx.db).authorize(scope, args.permission, args.procedure)
|
||||
} catch (error) {
|
||||
return problem(error)
|
||||
}
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user