{"version":3,"file":"index.cjs","names":["IAM_METHOD_ACTION_MAP","iamDefaultCsrfCheck","iamRunAdminAuthz","iamWithAdminAudit"],"sources":["../../../src/server/hono/index.ts"],"sourcesContent":["import type { IamEngine } from '../../core'\nimport type { AccessControl, IamRequest } from '../../core/types'\nimport {\n  IAM_METHOD_ACTION_MAP,\n  type IamAdminAudit,\n  iamDefaultCsrfCheck,\n  iamNoticeCsrfDefaultIfNeeded,\n  iamRunAdminAuthz,\n  iamWithAdminAudit,\n} from '../generic'\n\n/** Minimal IamHono context shape. */\ninterface HonoContext {\n  req: {\n    method: string\n    path: string\n    url: string\n    header(name: string): string | undefined\n    param(name: string): string | undefined\n  }\n  get(key: string): unknown\n  set(key: string, value: unknown): void\n  json(data: unknown, status?: number): Response\n  text(data: string, status?: number): Response\n}\n/** IamHono next function. */\ntype HonoNext = () => Promise<void>\n/** IamHono middleware function. */\ntype HonoMiddleware = (c: HonoContext, next: HonoNext) => Promise<Response | undefined>\n\n/** IamHono server integration types. Type-only namespace - zero bundle cost. */\nexport namespace IamHono {\n  /**\n   * Describes options for the IamHono {@link iamAccessMiddleware} and {@link iamGuard}.\n   *\n   * Every extractor has a sensible default.\n   *\n   * @template TScope - Constrains valid scope strings.\n   */\n  export interface IOptions<TScope extends string = string> {\n    /** Extracts the current user ID from the context. */\n    getUserId?: (c: HonoContext) => string | null\n    /** Derives the target resource from the context. */\n    getResource?: (c: HonoContext) => IamRequest.IResource\n    /** Derives the action being performed from the context. */\n    getAction?: (c: HonoContext) => string\n    /** Extracts environment context (IP, user-agent, etc.) from the context. */\n    getEnvironment?: (c: HonoContext) => IamRequest.IEnvironment\n    /** Determines the scope used for the access check. */\n    getScope?: (c: HonoContext) => TScope | undefined\n    /** Handles a denied request (defaults to 403 JSON). */\n    onDenied?: (c: HonoContext) => Response\n    /** Handles thrown errors during evaluation (defaults to 500 JSON). */\n    onError?: (err: Error, c: HonoContext) => Response\n  }\n\n  /**\n   * Required iamGuard callback for the IamHono admin router.\n   *\n   * Returning `false` (or throwing) blocks the request.\n   */\n  export type IAdminAuthorize = (c: HonoContext) => boolean | Promise<boolean>\n\n  /** Describes options for {@link iamBindAdminRouter}. `authorize` is required. */\n  export interface IAdminOptions extends IamAdminAudit.IOptions {\n    /** Required. Runs before every admin handler (read or write). */\n    authorize: IAdminAuthorize\n    /** Overrides the 401 unauthorized response. */\n    onUnauthorized?: (c: HonoContext) => Response\n    /** Overrides the 500 internal error response. */\n    onError?: (err: Error, c: HonoContext) => Response\n    /**\n     * Optional audit hook fired AFTER every mutation handler (PUT/POST/\n     * DELETE/PATCH) completes - success or failure. The hook is\n     * fire-and-forget: a slow or throwing implementation never blocks the\n     * request and can never alter the response. GET handlers do not fire it.\n     *\n     * See {@link IamAdminAudit.IOptions} for additional hardening knobs:\n     * `redactPath`, `onAuditHookError`, and `includeErrorMessage`.\n     */\n    onAdminMutation?: IamAdminAudit.Hook\n  }\n\n  /** Describes the minimal IamHono router surface used by {@link iamBindAdminRouter}. */\n  export interface IRouterLike {\n    get(path: string, handler: (c: HonoContext) => Promise<Response> | Response): unknown\n    put(path: string, handler: (c: HonoContext) => Promise<Response> | Response): unknown\n    post(path: string, handler: (c: HonoContext) => Promise<Response> | Response): unknown\n    delete(path: string, handler: (c: HonoContext) => Promise<Response> | Response): unknown\n  }\n}\n\n/** Extract environment from IamHono context using common headers. */\nfunction defaultEnv(c: HonoContext): IamRequest.IEnvironment {\n  return {\n    ip: c.req.header('cf-connecting-ip') ?? c.req.header('x-forwarded-for'),\n    userAgent: c.req.header('user-agent'),\n    timestamp: Date.now(),\n  }\n}\n\n/**\n * Builds IamHono middleware that runs `engine.can(...)` on every request.\n *\n * Replies 401 when no user is present and 403 when denied.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TRole - Constrains valid role strings.\n * @template TScope - Constrains valid scope strings.\n * @param engine - Provides the access engine to consult.\n * @param opts - Configures optional extractors and error hooks.\n * @returns A IamHono middleware function.\n * @example\n * ```ts\n * app.use('*', iamAccessMiddleware(engine, {\n *   getUserId: (c) => c.get('userId') as string | null,\n * }))\n * ```\n */\nexport function iamAccessMiddleware<\n  TAction extends string = string,\n  TResource extends string = string,\n  TRole extends string = string,\n  TScope extends string = string,\n>(engine: IamEngine<TAction, TResource, TRole, TScope>, opts: IamHono.IOptions<TScope> = {}): HonoMiddleware {\n  const {\n    // Read only from upstream-set `c.get('userId')`; never trust client headers.\n    getUserId = (c) => (c.get('userId') as string | undefined) ?? null,\n    getResource = (c) => {\n      const parts = c.req.path.split('/').filter(Boolean)\n      return { type: parts[0] ?? 'root', id: parts[1], attributes: {} }\n    },\n    getAction = (c) => IAM_METHOD_ACTION_MAP[c.req.method] ?? 'read',\n    getEnvironment = defaultEnv,\n    getScope,\n    onDenied = (c) => c.json({ error: 'Forbidden' }, 403),\n    onError = (_err, c) => c.json({ error: 'Internal server error' }, 500),\n  } = opts\n\n  return async (c, next) => {\n    const userId = getUserId(c)\n    if (!userId) return c.json({ error: 'Unauthorized' }, 401)\n\n    try {\n      const allowed = await engine.can(\n        userId,\n        getAction(c) as TAction,\n        getResource(c) as IamRequest.IResource<TResource>,\n        getEnvironment(c),\n        getScope?.(c),\n      )\n\n      if (!allowed) return onDenied(c)\n      await next()\n    } catch (err) {\n      return onError(err instanceof Error ? err : new Error(String(err)), c)\n    }\n  }\n}\n\n/**\n * Wires admin CRUD endpoints onto a IamHono router.\n *\n * `authorize` is required and runs before every handler. Throws when the\n * callback is missing.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TRole - Constrains valid role strings.\n * @template TScope - Constrains valid scope strings.\n * @param router - Provides the existing IamHono router instance.\n * @param engine - Provides the access engine whose `admin` operations are exposed.\n * @param opts - Must include `authorize`.\n * @returns The same router (chainable).\n * @throws Error when `opts.authorize` is not a function.\n * @example\n * ```ts\n * import { IamHono } from 'hono'\n * const admin = new IamHono()\n * iamBindAdminRouter(admin, engine, {\n *   authorize: (c) => isAdmin(c),\n *   onAdminMutation: (e) => auditLog.write(e),\n * })\n * app.route('/admin', admin)\n * ```\n * @example\n * Rate limiting is out of scope; compose at the mount point with a IamHono\n * middleware before the admin sub-app. Pseudocode:\n * ```ts\n * import { rateLimit } from 'some-hono-rate-limit'\n * app.use('/admin/*', rateLimit({ windowMs: 60_000, max: 30 }))\n * app.route('/admin', admin)\n * ```\n */\nexport function iamBindAdminRouter<\n  TAction extends string = string,\n  TResource extends string = string,\n  TRole extends string = string,\n  TScope extends string = string,\n>(\n  router: IamHono.IRouterLike,\n  engine: IamEngine<TAction, TResource, TRole, TScope>,\n  opts: IamHono.IAdminOptions,\n): IamHono.IRouterLike {\n  if (!opts || typeof opts.authorize !== 'function') {\n    throw new Error('[@gentleduck/iam] iamBindAdminRouter requires an `authorize` callback.')\n  }\n  const { authorize, onAdminMutation, redactPath, onAuditHookError, includeErrorMessage, csrfCheck } = opts\n  // Default to the built-in Sec-Fetch-Site check; pass `false` to disable.\n  const effectiveCsrfCheck = csrfCheck === false ? null : (csrfCheck ?? iamDefaultCsrfCheck)\n  iamNoticeCsrfDefaultIfNeeded(csrfCheck !== undefined)\n  const onUnauthorized = opts.onUnauthorized ?? ((c) => c.json({ error: 'Unauthorized' }, 401))\n  const onError = opts.onError ?? ((_, c) => c.json({ error: 'Internal server error' }, 500))\n\n  /** Read gate: no audit emission. */\n  const gate =\n    (handler: (c: HonoContext) => Promise<Response> | Response) =>\n    async (c: HonoContext): Promise<Response> => {\n      try {\n        if (!(await authorize(c))) return onUnauthorized(c)\n        return await handler(c)\n      } catch (err) {\n        return onError(err instanceof Error ? err : new Error(String(err)), c)\n      }\n    }\n\n  /**\n   * Mutation gate: identical to {@link gate} but emits an `onAdminMutation`\n   * event after the handler resolves or rejects. Uses try/finally so the\n   * hook fires even when the handler throws.\n   */\n  const mutate =\n    (\n      action: IamAdminAudit.Action,\n      target: IamAdminAudit.Target,\n      getTargetId: ((c: HonoContext) => string | undefined) | undefined,\n      handler: (c: HonoContext) => Promise<Response> | Response,\n    ) =>\n    async (c: HonoContext): Promise<Response> => {\n      // Shared CSRF + authorize phase.\n      const authz = await iamRunAdminAuthz(c, effectiveCsrfCheck, authorize)\n      if (authz.phase === 'forbidden') return c.json({ error: 'Forbidden (CSRF check failed)' }, 403)\n      if (authz.phase === 'unauthorized') return onUnauthorized(c)\n      if (authz.phase === 'error') return onError(authz.error, c)\n      try {\n        return await iamWithAdminAudit(\n          {\n            actor: authz.actor,\n            action,\n            target,\n            targetId: getTargetId?.(c),\n            method: c.req.method,\n            path: c.req.path,\n            onAdminMutation,\n            redactPath,\n            onAuditHookError,\n            includeErrorMessage,\n          },\n          () => Promise.resolve(handler(c)),\n        )\n      } catch (err) {\n        return onError(err instanceof Error ? err : new Error(String(err)), c)\n      }\n    }\n\n  router.get(\n    '/policies',\n    gate(async (c) => c.json(await engine.admin.listPolicies())),\n  )\n  router.get(\n    '/roles',\n    gate(async (c) => c.json(await engine.admin.listRoles())),\n  )\n  router.put(\n    '/policies',\n    mutate('replace', 'policy', undefined, async (c) => {\n      const body = (await (c as unknown as { req: { json(): Promise<unknown> } }).req.json()) as AccessControl.IPolicy<\n        TAction,\n        TResource,\n        TRole\n      >\n      await engine.admin.savePolicy(body)\n      return c.json({ ok: true })\n    }),\n  )\n  router.put(\n    '/roles',\n    mutate('replace', 'role', undefined, async (c) => {\n      const body = (await (c as unknown as { req: { json(): Promise<unknown> } }).req.json()) as AccessControl.IRole<\n        TAction,\n        TResource,\n        TRole,\n        TScope\n      >\n      await engine.admin.saveRole(body)\n      return c.json({ ok: true })\n    }),\n  )\n  router.post(\n    '/subjects/:id/roles',\n    mutate(\n      'create',\n      'role-assignment',\n      (c) => c.req.param('id'),\n      async (c) => {\n        const raw: unknown = await (c as unknown as { req: { json(): Promise<unknown> } }).req.json()\n        if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n          return c.json({ error: 'invalid body' }, 400)\n        }\n        const roleId = Reflect.get(raw, 'roleId')\n        const scope = Reflect.get(raw, 'scope')\n        if (typeof roleId !== 'string' || roleId.length === 0 || roleId.length > 128) {\n          return c.json({ error: 'invalid roleId' }, 400)\n        }\n        if (scope !== undefined && (typeof scope !== 'string' || scope.length === 0 || scope.length > 128)) {\n          return c.json({ error: 'invalid scope' }, 400)\n        }\n        await engine.admin.assignRole(c.req.param('id') as string, roleId as TRole, scope as TScope | undefined)\n        return c.json({ ok: true })\n      },\n    ),\n  )\n  router.delete(\n    '/subjects/:id/roles/:roleId',\n    mutate(\n      'delete',\n      'role-assignment',\n      (c) => c.req.param('id'),\n      async (c) => {\n        await engine.admin.revokeRole(c.req.param('id') as string, c.req.param('roleId') as TRole)\n        return c.json({ ok: true })\n      },\n    ),\n  )\n\n  return router\n}\n\n/**\n * Builds IamHono middleware that checks `(action, resourceType)` for the current\n * user, pulling the resource ID from the `:id` route param.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TRole - Constrains valid role strings.\n * @template TScope - Constrains valid scope strings.\n * @param engine - Provides the access engine to consult.\n * @param action - Specifies the action being performed.\n * @param resourceType - Specifies the resource type required for the check.\n * @param opts - Configures optional extractors and `scope` override.\n * @returns A IamHono middleware function.\n * @example\n * ```ts\n * app.delete('/posts/:id', iamGuard(engine, 'delete', 'post'), handler)\n * app.post('/admin/users', iamGuard(engine, 'manage', 'user', { scope: 'admin' }), handler)\n * ```\n */\nexport function iamGuard<\n  TAction extends string = string,\n  TResource extends string = string,\n  TRole extends string = string,\n  TScope extends string = string,\n>(\n  engine: IamEngine<TAction, TResource, TRole, TScope>,\n  action: TAction,\n  resourceType: TResource,\n  opts: Pick<IamHono.IOptions<TScope>, 'getUserId' | 'getEnvironment' | 'onDenied' | 'onError'> & {\n    scope?: TScope\n  } = {},\n): HonoMiddleware {\n  const {\n    // Read only from upstream-set `c.get('userId')`; never trust client headers.\n    getUserId = (c) => (c.get('userId') as string | undefined) ?? null,\n    getEnvironment = defaultEnv,\n    onDenied = (c) => c.json({ error: 'Forbidden' }, 403),\n    onError = (_err, c) => c.json({ error: 'Internal server error' }, 500),\n    scope,\n  } = opts\n\n  return async (c, next) => {\n    const userId = getUserId(c)\n    if (!userId) return c.json({ error: 'Unauthorized' }, 401)\n\n    try {\n      const allowed = await engine.can(\n        userId,\n        action,\n        { type: resourceType, id: c.req.param('id'), attributes: {} },\n        getEnvironment(c),\n        scope,\n      )\n\n      if (!allowed) return onDenied(c)\n      await next()\n    } catch (err) {\n      return onError(err instanceof Error ? err : new Error(String(err)), c)\n    }\n  }\n}\n"],"mappings":"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"}