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:
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.
Summary
Organizations can currently define an
overseesrelationship, 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:
Desired Behavior
For
GET /api/registry/org/:identifier:403 NOT_SAME_ORG_OR_SECRETARIAT.For
PUT /api/registry/org/:shortname:overseestop_level_rootprogram_dataCurrent Code Notes
overseesexists on registry org models:src/model/cnaorg.jssrc/model/secretariatorg.jssrc/model/rootorg.jsCurrent access checks are based on “same org or Secretariat”:
src/controller/registry-org.controller/registry-org.controller.jssrc/controller/org.controller/org.controller.jsExisting response logic already derives hierarchy metadata:
reports_to_relatedOrganizationsoverseesis currently listed as a Secretariat-only editable field in:src/constants/index.jsImplementation Notes
Add shared hierarchy authorization logic, likely in
src/utils/authContext.js, so controllers do not duplicate traversal behavior.Suggested helper behavior:
overseeslinks.The hierarchy check must be transitive. A direct
oversees.includes(target.UUID)check is not enough.Acceptance Criteria
403 NOT_SAME_ORG_OR_SECRETARIAT.overseestree do not cause infinite traversal.Test Coverage
Add integration tests using
/api/registry/orgendpoints.Recommended tests:
oversees: [Org B UUID]oversees: [Org C UUID]GET /api/registry/org/B.GET /api/registry/org/C.PUT /api/registry/org/Cfor allowed fields.oversees,top_level_root, orprogram_dataon Org C.