Resource hierarchy: convey ancestors in resource.properties and define subtree coverage

Open
#760 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
json, typescript
Domain
api, authorization

Research direction

Start with the Resource Properties and Metadata sections referenced in the issue, then review the related issues #597, #755, and erratum #601 to understand the open capability naming questions. The proposal is complete enough to compare a profile with a normative section and assess the ancestors versus parent options, but the issue leaves these decisions open. Done means agreeing on the hierarchy representation, subtree coverage semantics, depth limit, and capability signaling.

Written by the indexing model from the issue text.

Description

Problem

Many resource servers expose resources as a tree: for example service > host > database > table, or
organization > project > bucket > object. An access grant is often given on a node and applies to everything
below it ("alice may read host prod" covers every database of prod).

The Authorization API has no notion of containment. A Resource is a type, an id that is "scoped to the
type", and free properties. In a tree, an id is unique only below its parent (two hosts can each have a
database named analytics), so type and id alone cannot name a node, and a PDP that does not own the tree
cannot tell which grant applies.

Related issues and text:

  • #11 asked about complex or nested asset attributes in an early draft and was closed as no longer
    applicable. It is not about containment.
  • The transitivity recommendation for searches (§ Search) illustrates group membership only. #269 discussed
    a folder-contains-document example, and the text of PR #292 as first merged used one, but that example is
    not in the current text.
  • #329 (closed, won't fix) keeps type and id required in evaluations. A hierarchy convention has to keep
    them.

What we do today (outside the specification)

The PEP sends the chain of ancestors in resource.properties:

{
  "subject": { "type": "user", "id": "alice" },
  "action":  { "name": "query.run.read" },
  "resource": {
    "type": "database",
    "id": "analytics",
    "properties": {
      "ancestors": [ { "type": "service", "id": "db" }, { "type": "host", "id": "prod" } ]
    }
  }
}

ancestors is ordered from the root to the parent of the resource. Each entry is a Resource reference with
type and id. A grant on the resource or on any entry of the chain applies.

The specification says that receivers MUST ignore unknown fields in request and response bodies. It does not
say it for unknown keys inside properties, but the certification scenarios accept additional properties
(c-2-2-8). A PDP that does not know ancestors therefore ignores it, and decides on the resource alone.

Proposal

A small profile, "Resource Hierarchy", that only fixes what is now left open:

  1. The name and the shape of the property: resource.properties.ancestors, an array of {type, id} ordered
    from the root to the parent, with the rules above.
  2. The meaning for a PDP that supports the profile: a grant (or a policy) that applies to an ancestor applies
    to the resource. The profile says nothing about how the PDP stores its policies.
  3. A depth limit, to keep requests bounded (we use 4 levels below the service).
  4. A capability URN, urn:openid:authzen:capability:<name> by the convention of the existing profiles,
    announced in the PDP metadata. Profiles define a URN when they add response vocabulary that a PEP must
    understand and enforce (token issuance profile, § Discovery). Profiles that add only advisory request
    context keys or registered action names define none (token exchange and authorization claims). ancestors
    is a request key, but it is not advisory: a PDP that ignores it misses a grant on an ancestor, and, because
    ids are scoped below their parent, it can apply a grant on database:analytics of one host to the database
    of the same name on another host. A PEP therefore needs to know whether the PDP honours it.
Example

The PDP holds one grant: alice may query.run.read on host:prod of the service db.

A question on a database of prod. The chain names the host, so the grant on the host applies:

{
  "subject": { "type": "user", "id": "alice" },
  "action":  { "name": "query.run.read" },
  "resource": {
    "type": "database",
    "id": "analytics",
    "properties": {
      "ancestors": [ { "type": "service", "id": "db" }, { "type": "host", "id": "prod" } ]
    }
  }
}
{ "decision": true }

The same database name on another host. The ids are scoped below their parent, so the chain tells the two
apart and the grant does not apply:

{
  "subject": { "type": "user", "id": "alice" },
  "action":  { "name": "query.run.read" },
  "resource": {
    "type": "database",
    "id": "analytics",
    "properties": {
      "ancestors": [ { "type": "service", "id": "db" }, { "type": "host", "id": "staging" } ]
    }
  }
}
{ "decision": false }

A question on the host itself, which has the service as its only ancestor. The grant names this node, so it
applies. A question on the service has no ancestors and the grant does not apply, because a grant covers a
node and what is below it, never what is above:

{
  "subject": { "type": "user", "id": "alice" },
  "action":  { "name": "query.run.read" },
  "resource": {
    "type": "host",
    "id": "prod",
    "properties": { "ancestors": [ { "type": "service", "id": "db" } ] }
  }
}
{ "decision": true }

A PDP that supports the profile announces the capability in its metadata (the URN name is only an
illustration):

{
  "policy_decision_point": "https://pdp.example.com",
  "access_evaluation_endpoint": "https://pdp.example.com/access/v1/evaluation",
  "capabilities": [ "urn:openid:authzen:capability:resource-hierarchy" ]
}

A PEP that finds no such capability knows that a grant on an ancestor may not be honoured, and can fall back
to evaluating each node, or refuse to rely on subtree grants.

On the registry: the core defines it (Specification Required) and registers no URN. Profiles define URNs, and
whether these are registered is open in #597 and #755. The metadata member is capabilities (§ Metadata); the
text also says supported_capabilities, see the erratum #601.

Questions

  • Is a profile the right place for this, or do you prefer a short normative section next to "Resource
    Properties"?
  • ancestors (the whole chain) or parent (one level, recursive)? The chain avoids a lookup in the PDP; a
    recursive parent is shorter for deep trees.
  • Should the ids of the chain be required to be scoped (unique below their own parent), as in the example?
Dominant language
TypeScript
Stars
170
Forks
46
Avg merge
18h 5m
Merged PRs (30d)
43

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openid/authzen

All issues in openid/authzen

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.