[platform] OSIS authorization: proposal grounded in the freezone gate (please review) #310

Open
opened 2026-06-20 02:51:20 +00:00 by mik-tf · 0 comments
Owner

@timur @despiegk could you review this and let me know what you think?

Context

Migrating hero_biz to OSIS surfaced a gap we should decide on as a team: OSIS authenticates the caller (the request context carries the user id and claims) but does not authorize. The generated CRUD ignores the caller, so reads and writes like *_list and *_get are open to anyone who can reach the socket. This is the same class of issue freezone already hit and fixed. See the migration writeup in lhumina_code/home#309 for where this came up.

What freezone already does (I audited it)

freezone solved this without changing its codegen, as a small gate layer in the backend:

  • A single authoritative identity step (identity_bind.rs): the caller id is not trusted from the request params (it is attacker controllable). One middleware rewrites it to the server verified principal (verified proxy pubkey, then API key owner, then verified JWT subject) or strips it entirely if there is no verified principal. This closes the cross tenant IDOR where a caller sets someone else's id and passes every ownership check.
  • A boundary gate that runs after identity is bound and before the typed handler (tenant_boundary.rs): a per method table maps each leaky method to an ownership rule. The rule kinds are owner match (row.user_id equals caller), membership or role tier (for example company roles), caller equals a named param, and a recursive case that resolves an opaque target to its owning entity and applies that entity's rule. Default is fail closed, admin can bypass.

Links: identity_bind.rs and tenant_boundary.rs.

The gap for Hero

freezone's gate is a hand maintained table of methods. That works for one service but does not scale across many services and thousands of generated methods, which is exactly the problem we will have once the whole stack is on OSIS. So I think we want the same rules, but declared in the schema so the generator emits the gate, rather than a table per service.

Proposed direction (two steps)

  1. Carry over the freezone pattern as a reusable OSIS gate now: server assert the caller identity (we already have most of this in the request context) and apply a per rootobject owner match before the generated CRUD, fail closed, admin bypass via a claim. This is copyable today and unblocks any service that needs protection.
  2. Move the rule into the oschema as the end state: an owner field marker on a rootobject plus per method or per field access (owner read write, public read, hidden), so the macro generates the gate. The rule kinds to support are the ones freezone already enumerated: owner match, caller equals param, role or membership tier, recursive target, admin override.

The ask

Does the schema level direction look right to you, or would you rather keep a central per service table like freezone has today? Anything in the freezone model we should or should not carry over? If we agree on the direction I will draft the concrete schema annotations plus a reference gate so the Egypt team has it before they start migrating services in parallel.

@timur @despiegk could you review this and let me know what you think? ## Context Migrating hero_biz to OSIS surfaced a gap we should decide on as a team: OSIS authenticates the caller (the request context carries the user id and claims) but does not authorize. The generated CRUD ignores the caller, so reads and writes like `*_list` and `*_get` are open to anyone who can reach the socket. This is the same class of issue freezone already hit and fixed. See the migration writeup in https://forge.ourworld.tf/lhumina_code/home/issues/309 for where this came up. ## What freezone already does (I audited it) freezone solved this without changing its codegen, as a small gate layer in the backend: - A single authoritative identity step (`identity_bind.rs`): the caller id is not trusted from the request params (it is attacker controllable). One middleware rewrites it to the server verified principal (verified proxy pubkey, then API key owner, then verified JWT subject) or strips it entirely if there is no verified principal. This closes the cross tenant IDOR where a caller sets someone else's id and passes every ownership check. - A boundary gate that runs after identity is bound and before the typed handler (`tenant_boundary.rs`): a per method table maps each leaky method to an ownership rule. The rule kinds are owner match (row.user_id equals caller), membership or role tier (for example company roles), caller equals a named param, and a recursive case that resolves an opaque target to its owning entity and applies that entity's rule. Default is fail closed, admin can bypass. Links: [identity_bind.rs](https://forge.ourworld.tf/znzfreezone_code/znzfreezone_backend/src/branch/development/src/identity_bind.rs) and [tenant_boundary.rs](https://forge.ourworld.tf/znzfreezone_code/znzfreezone_backend/src/branch/development/src/tenant_boundary.rs). ## The gap for Hero freezone's gate is a hand maintained table of methods. That works for one service but does not scale across many services and thousands of generated methods, which is exactly the problem we will have once the whole stack is on OSIS. So I think we want the same rules, but declared in the schema so the generator emits the gate, rather than a table per service. ## Proposed direction (two steps) 1. Carry over the freezone pattern as a reusable OSIS gate now: server assert the caller identity (we already have most of this in the request context) and apply a per rootobject owner match before the generated CRUD, fail closed, admin bypass via a claim. This is copyable today and unblocks any service that needs protection. 2. Move the rule into the oschema as the end state: an owner field marker on a rootobject plus per method or per field access (owner read write, public read, hidden), so the macro generates the gate. The rule kinds to support are the ones freezone already enumerated: owner match, caller equals param, role or membership tier, recursive target, admin override. ## The ask Does the schema level direction look right to you, or would you rather keep a central per service table like freezone has today? Anything in the freezone model we should or should not carry over? If we agree on the direction I will draft the concrete schema annotations plus a reference gate so the Egypt team has it before they start migrating services in parallel.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/home_lhumina#310
No description provided.