Skip to content

Latest commit

 

History

History
104 lines (58 loc) · 23.3 KB

File metadata and controls

104 lines (58 loc) · 23.3 KB

ACAL XPath Profile: A Reviewer's Guide

Purpose

This document is a companion guide for anyone reviewing the ACAL XPath Profile (acal-xpath-v1.0.md, cited here as [XPath-ACAL]) who knows ACAL but has not necessarily worked with XPath, or followed how this profile's extension types plug into ACAL Core's attribute-selector mechanism.

It is intended to be useful to:

  1. TC reviewers checking this profile against the XACML 3.0 XPath-related mechanisms it succeeds (XACML 3.0 core's AttributeSelector and ContextSelectorId mechanism, and the content-selector attribute identifier the Hierarchical Resource Profile defined for that mechanism to consume)
  2. Implementers deciding whether, and how, to support XPath-based attribute extraction from XML Content
  3. Anyone reviewing the ACAL Hierarchical Resource Profile or Multiple Decision Profile who was told "see the XPath Profile" for content-selector and wants to understand what that dependency actually is

This guide is explanatory and informative. It does not replace [XPath-ACAL] itself, which is the normative document.


What This Profile Adds to ACAL Core

ACAL Core defines AttributeSelectorType and EntityAttributeSelectorType abstractly — an object that selects attribute values out of a Content object's Body property by some path expression, without specifying what language that path expression is written in. This profile is what makes that abstract mechanism concrete for XML: it defines XPathAttributeSelectorType and XPathEntityAttributeSelectorType (extensions of Core's types adding a ContextSelectorId property and a Namespaces property), a xpathExpression data type for carrying an XPath expression plus the category it applies to, its namespace bindings, and — optionally — its XPath version, request-wide and policy-wide XPath-version defaults types, and three XPath-based functions (Annex C.3.1) — xpath-node-count (returns the number of nodes an XPath expression selects), and the two actual comparisons, xpath-node-equal and xpath-node-match. It also defines urn:oasis:names:tc:acal:1.0:content-selector — the standard attribute identifier consumed by ContextSelectorId — even though the concept originates in the XACML 3.0 Hierarchical Resource Profile, because ACAL deliberately defines a borrowed identifier beside the mechanism that consumes it rather than where XACML happened to put it (see "content-selector is defined here, not in HRP" below).

This is the profile the ACAL Hierarchical Resource Profile's XML-document scheme and the ACAL Multiple Decision Profile's XPath-expression scheme both depend on for content-selector — if you were told "see the XPath Profile" while reviewing either of those, this is what they meant.


XACML 3.0 to ACAL: Key Changes

content-selector is defined here, not in HRP

In XACML 3.0, urn:oasis:names:tc:xacml:3.0:content-selector is defined by the Hierarchical Resource Profile, even though it is consumed by XACML core's ContextSelectorId mechanism. ACAL reverses that: the identifier is defined in this profile, alongside the ContextSelectorId mechanism that consumes it (Annex D.3), and the ACAL Hierarchical Resource Profile references this definition rather than restating it (issue #118, resolved 2026-07-28). What to check as a reviewer: if you are reviewing HRP or MDP and looking for where content-selector is defined (as opposed to referenced), it will not be in either of those documents — it is here.

XPath version identifiers changed, and XPath 1.0 is not supported

XACML 3.0's XPath 2.0 identifier (http://www.w3.org/TR/2007/REC-xpath20-20070123) is replaced by https://www.w3.org/TR/xpath20/. XPath 3.0 and 3.1 are newly supported (https://www.w3.org/TR/xpath-30/, https://www.w3.org/TR/xpath-31/), with no XACML 3.0 predecessor identifier to replace. XPath 1.0 is not a supported version at all — Section 6 states this plainly (its identifier is not a valid XPathVersion value), because XPath 1.0's result data model — node-set / boolean / number / string, not a sequence of items — is incompatible with how the rest of the profile, and Annex C.3.1's functions in particular, are now specified. What this settles (issue #134 review): unlike the xacml:3.0:acal:1.0: identifier deprecations (content-selector, the xpath-node-* functions), which get a "Deprecated Identifier" column in a Section 9.2 conformance table and a MAY-recognize-as-equivalent allowance, the old XPath 2.0 W3C URI is not a permitted alias: Section 6 states no equivalence, so it is not a valid ACAL XPathVersion value and an XACML 3.0 → ACAL translation must substitute the new URI. What to check: an ACAL policy, request, or response whose XPathVersion evaluates to the old URI is non-conformant — flag it; and confirm the serializer omit-rule in Section 5.3.5 (omit local XPathVersion only when it denotes the same identifier as the applicable Defaults) is not read as treating the two URIs as interchangeable.

The Defaults types lost their namespace bindings; xpathExpression values gained an optional local XPathVersion (issue #134)

Earlier drafts had XPathRequestDefaultsType and XPathPolicyDefaultsType (Section 5.3.1, Section 5.3.2) each carrying a required XPathVersion and a namespace-prefix table for the enclosing Request/Policy. steven-legg's issue #134 found a real defect in the table: XML permits the same prefix bound to different URIs in different parts of one document, which a single flat table cannot represent, so translating such XML to JACAL/YACAL was lossy. The fix:

  • Namespace bindings move to the expression. Both Defaults types lose their namespace-bindings property. In JACAL/YACAL an xpathExpression value (Section 5.3.5) and a selector each carry their own optional Namespaces mapping — present (as bindings) when the expression needs supplied bindings, absent or empty otherwise (there is no Defaults-level table for it to fall back to either way). XML is unchanged — prefixes resolve from ordinary in-scope namespaces, ancestor declarations included, exactly as in XACML 3.0 (XML's nested scoping never had the defect). The only new XML obligation is at the boundary: an implementation translating a value into JACAL/YACAL, or materializing one into a new document such as a response Notice, MUST carry the namespace context the expression relies on across.
  • XPathVersion becomes an optional property of XPathExpressionValueType. Both XPathRequestDefaultsType and XPathPolicyDefaultsType survive as XPathVersion-only. A value in a Request or Policy with no local XPathVersion takes it from the applicable Defaults object (as in XACML 3.0); a value in a decision response, or with no applicable Defaults, carries XPathVersion locally. The version is resolved once, when the value is constructed, and does not change if the value is later moved or copied — a request-originated value is not re-resolved against a policy's XPathPolicyDefaults when consumed there. There is no implicit "latest version" fallback; a value with neither a local version nor an applicable Defaults is a syntax error.

What to check: a reference anywhere in this repository to a namespace-bindings property (Namespace or Namespaces) on XPathRequestDefaultsType/XPathPolicyDefaultsType, or to XPathVersion or Namespaces being required on an xpathExpression value or a JACAL/YACAL selector, is stale content from before this change — both are optional. XPathRequestDefaultsType itself is not gone — an earlier iteration of this branch removed it and that framing may survive in stale notes.

XPathPolicyDefaults applies to nested policies (issue #139)

PolicyDefaults scope in ACAL Core §7.4 was clarified to extend into nested policies — a nested policy inherits an ancestor's PolicyDefaults of a given concrete type unless it declares its own. So an XPath selector Path, or a version-less xpathExpression value, in a policy with no XPathPolicyDefaultsType of its own takes its version from the nearest enclosing policy that has one (Section 5.3.1, Section 5.3.5). This matches how VariableDefinition scope already works (Core §7.13). No fixed default: if there is no XPathPolicyDefaultsType anywhere up the chain and no local XPathVersion, that is a syntax error. XACML 3.0 could not nest a <Policy>, so the case is new; the eager "resolve once, then freeze" rule for a value's effective version is unchanged — it just resolves against the nearest enclosing Defaults at construction time.

What to check: any earlier-draft wording that says a PolicyDefaults object's scope is "its own Policy" / "does not extend into nested child policies" / that a nested policy "MUST … carry its own XPathPolicyDefaultsType", or that cites Core "Section 7.5" for the scope rule (it is §7.4), is stale — it predates issue #139.

NamespaceDeclarationsType is a single mapping object in non-XML representations, and appears in more places now

NamespaceDeclarationsType (Section 5.2) is still not used in XACML v4.0/XML, which has native namespace support. In XML, both a selector's Path and an xpathExpression value's XPath resolve prefixes the same way: ordinary XML in-scope-namespace lookup, which can be satisfied by an ancestor declaration (every canonical XML Rule example binds md on the outer <Policy>, not on each element that uses it) — issue #134's defect was specific to a single flat namespace table in the non-XML representations, and XML's nested scoping never had it. In JACAL/YACAL, xpathExpression values and both selector types MAY carry a Namespaces property: a single NamespaceDeclarationsType object, realized as one mapping from prefix to namespace name ({"md": "urn:example:..."} in JSON, an equivalent mapping in YAML) rather than an array of {Prefix, Name} pair-objects — a shape change from before issue #134, made because the old array shape was reused so much more often once every value and selector needed its own copy that the array-of-objects form became noticeably more awkward than a plain mapping, in both formats. The property is optional: it is simply absent (or an empty mapping) when the expression needs no supplied bindings — all its names resolve without one, or namespaces are addressed via namespace-uri(), wildcards, or URIQualifiedNames — and there is no Defaults-level namespace table for an absent property to fall back to. What to check: if you see the Namespaces property (or a stale Namespace property) serialized as an array of pair-objects anywhere in this repository, or a Namespaces property declared inside an XACML/XML example (it never is — that's a JACAL/YACAL-only property), that's stale content from before issue #134. Do not flag an XML value or selector that relies on an ancestor xmlns:* as a defect — that is the correct, unchanged behavior. Do flag an XML xpathExpression value materialized into a response Notice whose XPath prefixes are not in scope anywhere in that response document.

Implementation-defined XPath behavior is pinned, not left open

Section 6 works through every item the W3C XPath 2.0/3.0/3.1 specifications leave implementation-defined (Unicode version, collations, implicit timezone, integer overflow behavior, and about a dozen more) and states ACAL's position on each — mostly "implementation defined," but with specific, load-bearing exceptions: the implicit timezone is fixed to UTC, XPath errors during attribute-selector evaluation become Indeterminate with processing-error, and implementations must support at least the 2025b IANA tz database. What to check: this section is long and easy to skim past, but it is where an interoperability bug would actually originate if two independently-written PDPs picked different implementation-defined behaviors for the same policy.

The XPath-based functions are restated in XPath 2.0+ terms — "node sequence," not "node-set"

Annex C.3.1's xpath-node-count / xpath-node-equal / xpath-node-match were inherited from XACML 3.0 still worded in XPath 1.0's "node-set" data model. Since XPath 1.0 is not a supported version and 2.0/3.0/3.1's data model has no "node-set" type, the functions are now defined over a sequence of nodes that each function treats as a set (repeated occurrences of the same node are ignored, order is immaterial); node sameness is the is relationship. Results are unchanged for any expression that selects nodes. Two cases XACML 3.0 left unstated are now defined, both returning Indeterminate with urn:oasis:names:tc:acal:1.0:status:processing-error: an argument expression that raises an XPath error, and one that evaluates to a sequence containing a non-node itemcount(//x), 1+1, a conditional that can yield a string, or (under XPath 3.1) an array such as [.//a]. What to check: this was a deferred item from the issue #134 review; confirm the three function definitions and the Annex C.3.1 preamble use "sequence" / "set of nodes", not "node-set", consistently; that the non-node-result rule reads as a processing error, not a syntax error (an expression returning atomic values is valid XPath, just not usable here); and that the preamble's four ordered steps give every argument combination a single outcome — in particular, a 1+1-style non-node argument (or an XPath error) yields Indeterminate even when the other argument's ContentType is absent, rather than being masked by an "either absent ⇒ false" rule (that XACML 3.0 rule is now step 1 + step 4, subordinate to steps 2–3).

xpath-node-match's arguments are swapped relative to XACML 3.0

XACML 3.0's xpath-node-match put the ancestor first: condition (2) matched "some node below any node in the first argument." That is now reversed — the first argument is the candidate node(s), the second is the node(s) it is tested against — to match the candidate-first, pattern-second order ACAL Core already uses for x500Name-match and rfc822Name-match (raised by steven-legg on PR #135/#138; x500Name-match(cn=John Smith,o=Medico Corp,c=US, O=Medico Corp,C=US) is the same pattern: specific value first, ancestor/pattern second). xpath-node-equal is unaffected (symmetric); xpath-node-count takes one argument. What to check: the Rule 1 worked example (§4.4.4.1) exercises this function via any-of — confirm the <AttributeDesignator> (content-selector, the candidate) now comes before the <Value XPath="md:record"> (the ancestor pattern) in both Rule1.xml and Rule1.json, and that the walkthrough prose's "first XPath expression" / "second XPath expression" wording matches the new order. Any external example or test vector built against the old (ancestor-first) order needs updating — this is a real behavioral change, not just wording.

The worked examples are XACML/JACAL only — there is no YACAL counterpart

This profile's own examples (Section 4.4) pair XACML/XML with JACAL/JSON for every rule and request — but there is no YACAL/YAML worked example anywhere in the document, and no Request.yaml/Rule*.yaml file in examples/acal-xpath/. What to check: if you are verifying this profile is representation-complete, YAML is the one representation with no worked example to check against; the schema and identifier artifacts are genuinely present for all three representations (see below), but only two of the three get a worked illustration.


Published Artifact Set

Unlike the Hierarchical Resource Profile and Multiple Decision Profile, this profile does introduce new schema artifacts, in all three representations:

  • acal-xpath-v1.0.md — the normative specification (this guide's subject)
  • acal-xpath-v1.0-reviewer-guide.md — this document
  • acal-xpath-xml-v4.0-schema.xsd, acal-xpath-json-v1.0-schema.json, acal-xpath-yaml-v1.0-structure.schema.yaml — the concrete schema for XPathAttributeSelectorType/XPathEntityAttributeSelectorType in each representation, plus XPathExpressionValueType in JSON/YAML only (see below)
  • acal-xpath-xml-v4.0-identifiers.xml, acal-xpath-json-v1.0-identifiers.json, acal-xpath-yaml-v1.0-identifiers.yaml — the identifier reference artifacts

What to check: confirm the two selector types actually have parity across all three schema files — this guide did not re-derive full type-by-type parity line by line; a careful review should. Do not expect parity for XPathExpressionValueType: the XSD has no complex type for it at all (XML xpathExpression values use Core's generic ValueType, whose xs:anyAttribute wildcard already permits arbitrary attributes like XPathVersion without a dedicated schema addition), while JSON/YAML define it explicitly. Its XPathVersion is optional in all three (issue #134) — a value in a Request/Policy may take the version from XPathRequestDefaults/XPathPolicyDefaults — but a value in a response, or one with no applicable Defaults, MUST carry it locally, and that contextual rule is prose-only in every representation. (The Namespaces property is JACAL/YACAL-only; in XML the prefixes resolve from ordinary in-scope namespaces. Note that neither the XSD nor a plain XML parser checks a prefix that appears only inside the XPath string — such a Value is still well-formed XML — so an XML example with an undeclared XPath prefix will not be caught mechanically either. See Section 5.3.5's Namespaces prose.)


Worked Example Walkthrough

[XPath-ACAL] Section 4.4 gives a complete worked scenario, not a single isolated snippet: one example medical-record XML document, one example request context, and three example rules (Section 4.4.4 actually defines three — Rule 1 through Rule 3 — even though this section's own lead sentence used to say "four separate rules are defined"; that miscount was found while writing this guide and has been corrected in the specification) — each pair shown in both XACML/XML and JACAL/JSON. What to check: the request context example demonstrates content-selector in practice — Section 4.4.2 shows a urn:oasis:names:tc:acal:1.0:content-selector attribute whose value names the node the rules' XPathAttributeSelector elements then reference via ContextSelectorId, which is the concrete mechanism Section 5.3.3 describes abstractly. Rule 1 (Section 4.4.4.1) is the simplest — a single XPathAttributeSelector and an xpath-node-match comparison — and is the one to read first if this is your first time seeing this profile's mechanism in a complete policy. There is no YACAL/YAML version of any of these examples; see above.


Review Guidance for ACAL Reviewers New to This Profile

  • Confirm content-selector really is defined here and not duplicated in HRP. Grep both documents; the identifier should appear defined exactly once (in this profile's Annex D.3) and referenced everywhere else.
  • Check the XPath-version changes carefully — two different kinds, easy to conflate. XPath 1.0 is dropped outright: not a supported version, its identifier not a valid XPathVersion value (its result data model is incompatible with the sequence model the rest of the profile uses). Separately, the XACML 3.0 XPath 2.0 identifier is replaced by a new URI for the same version — not the old URI carried forward, and not a recognized alias for the new one.
  • Treat Section 6's implementation-defined-items walkthrough as normative, not filler, even though it reads like a checklist copied from the W3C spec. The UTC-timezone pin and the Indeterminate/processing-error error-handling rule are the two easiest to miss and the two most likely to cause real interoperability failures if skipped.
  • Don't assume representation parity from the artifact list alone. Schema and identifier files exist for XML, JSON, and YAML, but the worked examples only cover XML and JSON — confirm the YAML schema is at least self-consistent even without a worked example to check it against.
  • If you are reviewing HRP or MDP and were told to "see the XPath Profile" for content-selector, this is that profile; you should find one definition here, not a fresh one.
  • Checking issue #134 compliance in an example is not just "look for Namespaces." Things to actually verify, none of which "the JSON/YAML is schema-valid" alone confirms: (1) in JACAL/YACAL, an xpathExpression value that uses namespace-prefixed names carries a Namespaces mapping supplying them (it is legitimately absent only when the expression needs no bindings — there is no {}-vs-omitted distinction to police); (2) every JACAL/YACAL selector (XPathAttributeSelectorType/XPathEntityAttributeSelectorType) that needs bindings carries its own Namespaces the same way; (3) an xpathExpression value with no local XPathVersion is only valid where an XPathRequestDefaults/XPathPolicyDefaults is in scope — a version-less value in a response Notice/AttributeAssignment, or in a bare fragment with no Defaults, is a defect (this is prose-only — no schema catches it); (4) a value that does carry a local XPathVersion should not be "corrected" to match a nearby Defaults — the local one wins and is deliberate; (5) an XML value or selector whose XPath prefixes resolve against an ancestor xmlns:* is correct, unchanged behavior — do not flag it; but an xpathExpression value materialized into a response whose prefixes are in scope nowhere in that response is a defect; (6) a duplicate key in a serialized Namespaces mapping is a real defect even though no JSON Schema validator will catch it (see Section 5.2's SHALL).

Bottom Line

This profile turns ACAL Core's abstract attribute-selector mechanism into a concrete XPath-based one, defines content-selector on behalf of every profile that needs it (not just itself), pins down XPath's implementation-defined behavior instead of leaving it as a portability trap, and ships schema and identifier artifacts for all three representations — but worked examples for only two of the three (XML and JSON; YAML has none).