Skip to content

Security model — positions, permission sets, sharing rules #8

Description

@os-warren

Every object already states its OWD; this is the layer that opens the right doors on top of private.

Files you own

  • src/security/positions.ts, src/security/permission-sets.ts, src/security/sharing-rules.ts (new), pushed into the three arrays in src/security/index.ts
  • test/security.test.ts (new)

Positions (flat — ADR-0090 D3, there is no position tree)

Position Who
duly_member everyone who owns duties
duly_manager anyone with reports or a unit
duly_admin catalog owners and rollout admins

Permission sets

Author with definePermissionSet({ name, label, objects: { … }, fields, tabPermissions, systemPermissions }).

duly_member

  • duly_task: create/read/edit, readScope: 'own', writeScope: 'own'
  • duly_duty: read own; create own (source: 'self')
  • duly_log_entry: full control of own, readScope: 'own'
  • duly_catalog_item: read only
  • duly_assignment: read the ones they are on

duly_manager — member, plus

  • duly_task, duly_duty: readScope: 'unit_and_below', writeScope stays 'own'. A manager reads everything below them and writes none of it. This is not an oversight to close later: the product's position is that a manager's only write is assigning, and the permission set is where that gets enforced rather than merely designed.
  • duly_assignment: create/edit own

duly_admin — manager, plus

  • duly_catalog_item: full control
  • duly_duty: readScope: 'org', and edit for corrections

duly_log_entry — the one that must not leak

readScope: 'own' for every position including admin. The only widening is the record's own visibility: 'manager', which a criteria sharing rule grants to that person's manager and nobody else. No unit scope, no org scope, no admin override.

A log people believe their skip-level can read is a log nobody keeps, and the module stops producing the record it exists to produce.

Sharing rules

defineSharingRule({ type: 'criteria', … }):

  • duly_log_entry where visibility = 'manager' → recipient: the owner's manager
  • duly_catalog_item is already public_read; no rule needed

The open-edition caveat — read before you debug

readScope: 'own_and_reports' | 'unit' | 'unit_and_below' | 'org' (ADR-0057) are resolved by @objectstack/security-enterprise, which enterprise deployments carry. This repo runs on the open edition, where they fall back to owner-only, silently, with no error.

That means a manager view in your dev environment will show you only your own rows. That is expected, not a bug — do not chase it, do not build an application-level fallback, and do not add hierarchy-security to requires (it would fail an open-edition boot). Write the metadata correctly for the enterprise runtime and assert the authored scopes in tests rather than the resolved rows.

Acceptance

  • every permission set asserts its declared readScope/writeScope per object in test/security.test.ts
  • no position anywhere grants duly_log_entry a scope wider than own
  • no position grants a write scope on duly_task/duly_duty wider than own
  • duly_manager and duly_admin inherit rather than restate duly_member's grants
  • a short section in docs/product/data-model.md or a new docs/deployment/security.md states the enterprise-edition requirement for a real rollout

Gates

pnpm validate && pnpm typecheck && pnpm test && pnpm build.

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions