Skip to content

DRAFT: Use oversees Org Hierarchy for Registry Org Access #1933

Description

@david-rocca

Summary

Organizations can currently define an oversees relationship, but that relationship is not used for authorization. We should allow an organization to view and update org records for organizations it oversees, including transitive descendants.

Example:

  • Org A oversees Org B
  • Org B oversees Org C
  • Org A should be allowed to access Org B and Org C

Desired Behavior

For GET /api/registry/org/:identifier:

  • Same-org users can still view their own org.
  • Secretariat can still view any org.
  • Users from an ancestor org can view descendant orgs.
  • Unrelated orgs still receive 403 NOT_SAME_ORG_OR_SECRETARIAT.

For PUT /api/registry/org/:shortname:

  • Secretariat can still update any org.
  • Same-org admins can still update their own org under existing rules.
  • Admins from an ancestor org can update descendant org records.
  • Ancestor org admins must still be blocked from editing Secretariat-only fields such as:
    • oversees
    • top_level_root
    • program_data

Current Code Notes

  • oversees exists on registry org models:

    • src/model/cnaorg.js
    • src/model/secretariatorg.js
    • src/model/rootorg.js
  • Current access checks are based on “same org or Secretariat”:

    • src/controller/registry-org.controller/registry-org.controller.js
    • src/controller/org.controller/org.controller.js
  • Existing response logic already derives hierarchy metadata:

    • reports_to
    • _relatedOrganizations
  • oversees is currently listed as a Secretariat-only editable field in:

    • src/constants/index.js

Implementation Notes

Add shared hierarchy authorization logic, likely in src/utils/authContext.js, so controllers do not duplicate traversal behavior.

Suggested helper behavior:

  • Resolve requester org UUID.
  • Resolve target org UUID.
  • Return true if requester org is the same as target org.
  • Return true if requester org is an ancestor of target org through one or more oversees links.
  • Return false for unrelated orgs.
  • Detect cycles to avoid infinite traversal.

The hierarchy check must be transitive. A direct oversees.includes(target.UUID) check is not enough.

Acceptance Criteria

  • Org A can view Org B when A directly oversees B.
  • Org A can view Org C when A oversees B and B oversees C.
  • Org A admin can update Org B and Org C using existing non-Secretariat update rules.
  • Org A admin cannot update Secretariat-only fields on Org B or Org C.
  • Org C cannot access Org A unless explicitly configured through its own hierarchy.
  • Unrelated orgs continue to receive 403 NOT_SAME_ORG_OR_SECRETARIAT.
  • Secretariat behavior is unchanged.
  • Same-org behavior is unchanged.
  • Cycles in the oversees tree do not cause infinite traversal.

Test Coverage

Add integration tests using /api/registry/org endpoints.

Recommended tests:

  • Create Org A, Org B, and Org C.
  • Secretariat configures:
    • Org A oversees: [Org B UUID]
    • Org B oversees: [Org C UUID]
  • Org A user can GET /api/registry/org/B.
  • Org A user can GET /api/registry/org/C.
  • Org A admin can PUT /api/registry/org/C for allowed fields.
  • Org A admin cannot update oversees, top_level_root, or program_data on Org C.
  • Org C user cannot access Org A.
  • An unrelated org cannot access Org B or Org C.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Needs Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions