Skip to content

Per-organization tenant/ownership authorization for Clients, Organizations, OrganizationUsers #79

Open

Description

@brinehart

Follow-up from #47. #47 closes the authentication hole (requires an authenticated caller and wires up UseAuthentication/UseAuthorization). It deliberately does not implement per-organization tenant/ownership checks, which are their own unit of work. This issue tracks that.

Problem

Once #47 lands, every Client/Organization/OrganizationUser endpoint requires authentication, but any authenticated caller can still read or modify any organization's data. There is no check that the caller belongs to the organization that owns the resource. The // TODO - Add [Policy(typeof(T), Permission.X)] comments in the service files describe this gap.

Identity -> organization mapping (design from the #47 spec)

  • The JWT subject is the OAuth provider user id; OrganizationUser.UserId (a Guid) aligns with it.
  • OrganizationUser <-> Organization is many-to-many via OrganizationUserOrganization.
  • Client.OrganizationId ties a client to its org.
  • A caller's org set: db.OrganizationUsers.Where(ou => ou.UserId == callerGuid).SelectMany(ou => ou.Organizations). A caller may operate on a client only if client.OrganizationId is in that set; on an org only if its id is in that set.

Blockers to resolve as part of this work

  1. No code reads claims from ServerCallContext/HttpContext yet; the claim name holding the user Guid is not established in code.
  2. The [Policy(typeof(T), Permission.X)] attribute, the Permission enum, and the per-resource policy infrastructure the TODOs reference do not exist.
  3. OrganizationUser.UserId is an [EncryptColumn] field, so equality lookups on it need an encryption-aware query path.

Acceptance

  • A claims-reading convention that resolves the caller's user Guid.
  • A resource/permission policy mechanism (or equivalent) enforcing the org-membership check above on all three services.
  • Tests: a caller in org A cannot read/modify org B's clients, orgs, or users; a caller in the owning org can.
  • CI gate green (scripts/check.sh).

Referenced by #47.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Should FixRisky code without a test, real slowness, duplication, or code that should not exist

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions