Skip to content

feat(files): In-App Policy support#197

Draft
atilafassina wants to merge 7 commits intomainfrom
files/service-principal-policies
Draft

feat(files): In-App Policy support#197
atilafassina wants to merge 7 commits intomainfrom
files/service-principal-policies

Conversation

@atilafassina
Copy link
Contributor

Summary

  • Policy system: Introduces FilePolicy, a per-volume function (action, resource, user) → boolean | Promise<boolean> that gates every file operation before the Databricks API call is made. Ships with built-in helpers (policy.publicRead(), policy.allowAll(), policy.denyAll()) and combinators (all, any, not) for composition.
  • Service-principal execution on HTTP routes: HTTP routes now always execute as the service principal instead of requiring OBO. Policies become the only app-level gate that distinguishes between users on HTTP routes — the user identity is extracted from the x-forwarded-user header and passed to the policy.
  • Default to publicRead(): Volumes without an explicit policy default to read-only access. A startup warning encourages setting an explicit policy.

Motivation

Previously the files plugin relied entirely on OBO (on-behalf-of) token forwarding and Unity Catalog grants to restrict access. This meant:

  1. There was no app-level way to restrict what individual users could do (e.g. allow reads but deny uploads).
  2. Revoking a user's UC WRITE_VOLUME grant had no effect on HTTP routes since the SP's credentials were used anyway.

Policies close this gap by giving developers a composable, per-volume authorization layer evaluated before any API call.

Key changes

File What changed
packages/appkit/src/plugins/files/policy.ts New module — FileAction, FileResource, FilePolicyUser types, PolicyDeniedError, and the policy combinator namespace
packages/appkit/src/plugins/files/plugin.ts Wired policy checks into every route handler and the programmatic VolumeHandle API. Removed isInUserContext gating in favor of SP-first execution + policy enforcement
packages/appkit/src/plugins/files/types.ts Added policy?: FilePolicy to VolumeConfig
packages/appkit/src/index.ts Re-exports policy from the top-level barrel
apps/dev-playground/server/index.ts Updated to use policy.allowAll() on the default volume
docs/docs/plugins/files.md Full documentation: permission model diagram, built-in policies, combinators, custom policies, enforcement semantics

Test scenarios (via dev-playground)

  1. policy.allowAll() volume — all CRUD operations succeed (list, read, upload, delete, mkdir)
  2. policy.publicRead() volume (default) — reads succeed, writes return 403
  3. policy.denyAll() volume — all operations return 403
  4. No x-forwarded-user header in production — returns 401 before policy is evaluated
  5. Async policy — policy returning Promise<boolean> is awaited correctly
  6. Programmatic API with policyappkit.files("vol").asUser(req).list() enforces policy with user identity; appkit.files("vol").list() enforces with { isServicePrincipal: true }
  7. Upload size in resourcecontent-length is passed as resource.size to the policy function
  8. Combinatorspolicy.all() short-circuits on first deny, policy.any() short-circuits on first allow, policy.not() inverts

Test plan

  • Unit tests for policy combinators (policy.test.ts — 136 lines, covers all helpers + async + PolicyDeniedError)
  • Unit tests for plugin policy enforcement (plugin.test.ts — 376 new lines covering routes, SDK API, SP identity, user identity, 401/403 responses)
  • Existing plugin tests updated to work with the new policy-first model
  • Manual verification against a Databricks workspace via pnpm dev with the dev-playground

This pull request was AI-assisted by Isaac.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant