Documentation
¶
Overview ¶
Package confine implements per-request root confinement for the /api/files surface. It lets a host app (e.g. work.example.com proxying the embedded filex explorer) lock a caller into a single sub-folder so a multi-tenant deploy can never let one project read or mutate another's files.
The confinement root comes from two trusted sources, combined narrowest-wins:
- An API token's `root:<adapter>://<rel>` scope — the HARD ceiling. The browser never holds the token (the host injects it server-side), so a token-confined caller cannot escape its root.
- The `X-Filex-Root: <adapter>://<rel>` request header — narrows further within the token root (or, absent a token root, sets the root). Set by the host's trusted proxy per request; a stray client header can only narrow, never widen past the token root.
Enforcement is a single chi middleware that rewrites/validates every path-bearing field of the request (query `?path=` and the JSON body keys BodyPathKeys lists, matched case-insensitively as encoding/json matches them). Anything outside the root is rejected 403; a root/empty path is rewritten to the confined folder so listings open there. It covers an endpoint only through the keys it knows: a guard test holds every handler request body to that list (api/handlers/confine_keys_test.go).
The body is read as JSON whatever its Content-Type, because that is how the handlers read it; only the routes whose body is the bytes of a file (rawBodyRoutes) are passed on unread.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrBadBody = errors.New("bad json")
ErrBadBody is returned for a body that begins as a JSON object and does not parse as one: what this layer cannot read it cannot hold to the root.
var ErrBodyTooLarge = errors.New("request body too large")
ErrBodyTooLarge is returned for a body that begins as a JSON object and is larger than maxBody: past the cut its keys would go unseen.
var ErrOutOfRoot = errors.New("path outside confined root")
ErrOutOfRoot is returned when a requested path escapes the confinement root.
Functions ¶
func BodyPathKeys ¶ added in v0.50.0
func BodyPathKeys() []string
BodyPathKeys lists every JSON body key Middleware confines, spelled "key" (a path), "key[]" (a list of paths) and "key[].field" (a list of objects whose field is a path) - what the handlers' guard test holds their request bodies to.
func HoldBody ¶ added in v0.52.0
HoldBody holds a request body a handler reads as JSON to the root exactly as Middleware holds one: the same keys, rewritten or checked the same way, refused with ErrOutOfRoot, and an object that does not parse refused with ErrBadBody. A body Middleware has already held passes through unchanged.
func Middleware ¶
Middleware enforces the effective root on every /api/files request. Mount it AFTER the auth middleware so the token is on the context.
func Refuse ¶ added in v0.52.0
func Refuse(w http.ResponseWriter)
Refuse writes Middleware's own answer to a path outside the root, so a handler that refuses one answers byte for byte what Middleware answers.
func RefuseFor ¶ added in v0.53.0
func RefuseFor(w http.ResponseWriter, err error)
RefuseFor writes Middleware's own answer to an error HoldBody returned: 403 for a path outside the root, 400 for a body that does not parse.
Types ¶
type Root ¶
Root is a confinement scope: a single storage adapter + a clean relative prefix within it (no leading/trailing slash; "" == the storage root).
func CallerRoot ¶ added in v0.52.0
CallerRoot is the confinement root of the call on ctx: the one Middleware stashed (a token's `root:` narrowed by X-Filex-Root) or, where a request did not pass through Middleware (the /api/ai surfaces), the token's own `root:` scope (RootFromToken). ok=false for an unconfined caller.
func FromRequest ¶
FromRequest derives the effective confinement root for r: the token's `root:` scope narrowed by an X-Filex-Root header. Returns ok=false when the request is unrestricted (no token root and no header) — admins / the native panel keep full access.
func ParseRoot ¶
ParseRoot exposes parseRoot to the protocol servers, which read a confinement out of an API token's `root:` scope themselves — they authenticate outside the HTTP chain, so the middleware in this package never runs for them. Returns ok=false for an empty or storage-less spec.
func RootFromToken ¶
RootFromToken derives a confinement Root from the API token on ctx alone (its `root:` scope), without an *http.Request. The AI surface (/api/ai) does not pass through Middleware, so aiOps calls this to honor a token's path ceiling. Returns ok=false for unconfined tokens / cookie sessions.
func (Root) EnforcePath ¶
EnforcePath is the exported form of enforce: validate/normalize a client path against the root, returning the qualified path or ErrOutOfRoot. The AI surface (aiOps) uses it directly since it bypasses Middleware.