Skip to content

Commit

Permalink
feat: create server only access control for loaders and actions
Browse files Browse the repository at this point in the history
  • Loading branch information
IncognitaDev committed Jan 16, 2025
1 parent a3758ba commit 0aa1271
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 7 deletions.
13 changes: 9 additions & 4 deletions blocks/action.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// deno-lint-ignore-file no-explicit-any
import { applyProps, type FnProps } from "../blocks/utils.tsx";
import {
applyProps,
type FnProps,
GateKeeperAccess,
} from "../blocks/utils.tsx";
import JsonViewer from "../components/JsonViewer.tsx";
import type { Block, BlockModule, InstanceOf } from "../engine/block.ts";
import { gateKeeper } from "./utils.tsx";

export type Action = InstanceOf<typeof actionBlock, "#/root/actions">;

export type ActionModule<
export interface ActionModule<
TProps = any,
TResp = any,
> = BlockModule<FnProps<TProps, TResp>>;
> extends BlockModule<FnProps<TProps, TResp>>, GateKeeperAccess {}

const actionBlock: Block<ActionModule> = {
type: "actions",
Expand All @@ -17,7 +22,7 @@ const actionBlock: Block<ActionModule> = {
>(
mod: ActionModule<TProps>,
) => [
applyProps(mod),
applyProps(gateKeeper(mod)),
],
defaultPreview: (result) => {
return {
Expand Down
10 changes: 8 additions & 2 deletions blocks/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
applyProps,
type FnContext,
type FnProps,
gateKeeper,
type GateKeeperAccess,
type RequestState,
type SingleFlightKeyFunc,
} from "./utils.tsx";
Expand All @@ -28,7 +30,7 @@ export type Loader = InstanceOf<typeof loaderBlock, "#/root/loaders">;
export interface LoaderModule<
TProps = any,
TState = any,
> extends BlockModule<FnProps<TProps>> {
> extends BlockModule<FnProps<TProps>>, GateKeeperAccess {
/**
* Specifies caching behavior for the loader and its dependencies.
*
Expand Down Expand Up @@ -335,7 +337,11 @@ const loaderBlock: Block<LoaderModule> = {
adapt: <TProps = any>(mod: LoaderModule<TProps>) => [
wrapCaughtErrors,
(props: TProps, ctx: HttpContext<{ global: any } & RequestState>) =>
applyProps(wrapLoader(mod, ctx.resolveChain, ctx.context.state.release))(
applyProps(
gateKeeper(
wrapLoader(mod, ctx.resolveChain, ctx.context.state.release),
),
)(
props,
ctx,
),
Expand Down
32 changes: 31 additions & 1 deletion blocks/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import type { StatusCode as Status } from "@std/http/status";
import type { JSX } from "preact";
import type { AppManifest, ImportMap } from "../blocks/app.ts";
import { isInvokeCtx } from "../blocks/loader.ts";
import { isInvokeCtx, LoaderModule } from "../blocks/loader.ts";
import type { InvocationFunc } from "../clients/withManifest.ts";
import { withSection } from "../components/section.tsx";
import type {
Expand All @@ -27,6 +27,11 @@ import type { InvocationProxy } from "../utils/invoke.types.ts";
import { type Device, deviceOf, isBot as isUABot } from "../utils/userAgent.ts";
import type { HttpContext } from "./handler.ts";
import type { Vary } from "../utils/vary.ts";
import { ActionModule } from "./action.ts";

export interface GateKeeperAccess {
visibility?: "server" | "public";
}

export type SingleFlightKeyFunc<TConfig = any, TCtx = any> = (
args: TConfig,
Expand Down Expand Up @@ -138,6 +143,7 @@ export const fnContextFromHttpContext = <TState = {}>(
},
};
};

/**
* Applies the given props to the target block function.
*
Expand Down Expand Up @@ -248,3 +254,27 @@ export const buildImportMap = (manifest: AppManifest): ImportMap => {
);
return buildImportMapWith(manifest, builder);
};

export const gateKeeper = (
{
default: handler,
visibility = "public",
...rest
}: BlockModule & GateKeeperAccess,
) => {
return {
...rest,
default: async (
props: Parameters<typeof handler>[0],
req: Request,
ctx: FnContext<unknown, any>,
): Promise<ReturnType<typeof handler>> => {
if (visibility === "server" && !ctx.isInvoke) {
return new Response(null, {
status: 403,
});
}
return await handler(props, req, ctx);
},
};
};

0 comments on commit 0aa1271

Please sign in to comment.