[ 
https://issues.apache.org/jira/browse/CAMEL-25028?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Claus Ibsen resolved CAMEL-25028.
---------------------------------
    Resolution: Fixed

Fixed via https://github.com/apache/camel/pull/26974 (merged to main for 
4.23.0).

_Claude Code on behalf of davsclaus_

> camel-openfga: new component for relationship-based (Zanzibar/ReBAC) 
> authorization decisions
> --------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-25028
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25028
>             Project: Camel
>          Issue Type: New Feature
>            Reporter: Andrea Cosentino
>            Assignee: Andrea Cosentino
>            Priority: Major
>              Labels: authorization, openfga, security
>             Fix For: 4.23.0
>
>
> h2. Why
> Camel now has two of the three pieces of a modern zero-trust authorization 
> story:
> * {{camel-spiffe}} answers *who is the caller* (workload identity, X.509-SVID 
> / JWT-SVID).
> * {{camel-opa}} answers *what do the rules say* (policy-as-code, Rego, 
> evaluated per Exchange).
> The missing third piece is *what is this subject's relationship to this 
> resource*. Policy-as-code is
> a poor fit for that question: expressing "anne can read document:budget 
> because she owns the folder
> it lives in" in Rego means shipping the whole relationship graph into the 
> policy input, which does
> not scale past a handful of tuples.
> [OpenFGA|https://openfga.dev] (CNCF, an implementation of Google's Zanzibar 
> paper) is the widely
> adopted answer: relationship tuples {{(user, relation, object)}} are stored 
> in the decision point, and
> the route asks it a question instead of shipping it data. Today a Camel route 
> that needs this has to
> hand-roll an SDK call in a {{.process()}} block, which is exactly the 
> situation {{camel-opa}} was
> created to end -- and hand-rolled authorization code is where the interesting 
> mistakes live.
> h2. What
> A new {{camel-openfga}} component, deliberately built as a companion to 
> {{camel-opa}} and sharing its
> shape and its security posture, so an operator who knows one knows the other.
> *Producer operations* (URI: {{openfga:operation}}):
> || Operation || Purpose ||
> | {{check}} | the authorization decision: may this subject use this relation 
> on this object |
> | {{batchCheck}} | many checks in one round trip, for filtering a collection |
> | {{listObjects}} | which objects of a type this subject may reach through a 
> relation |
> | {{listRelations}} | which of a set of relations this subject has on one 
> object |
> | {{listUsers}} | which subjects hold a relation on one object |
> | {{writeTuples}} | grant: add relationship tuples (a route that creates a 
> resource also grants access to it) |
> | {{deleteTuples}} | revoke: remove relationship tuples |
> *Route enforcement*: an {{OpenFgaSecurityPolicy}} implementing 
> {{AuthorizationPolicy}}, so a route is
> guarded declaratively rather than by an {{if}} in a processor -- the direct 
> counterpart of
> {{OpaSecurityPolicy}}:
> {code:java}
> from("platform-http:/orders")
>     .policy(openFgaPolicy)     // a deny throws CamelAuthorizationException
>     .to("direct:handleOrder");
> {code}
> h2. Security posture
> Carried over from {{camel-opa}}, because the same class of mistake applies:
> * *Fails closed.* An unreachable or erroring OpenFGA server denies. 
> {{failOpen}} exists, is off by
>   default and is annotated {{security = "insecure:dev"}}.
> * *The question is not selectable by the message.* {{storeId}}, 
> {{authorizationModelId}} and
>   {{relation}} come from the endpoint only. An inbound message cannot 
> downgrade the relation it is
>   judged against (e.g. {{owner}} -> {{reader}}) or point the check at a 
> different store or an older
>   model revision.
> * *Decision headers are cleared on entry*, written on every evaluation, and 
> never read back as input,
>   so a verdict a message arrived with never survives -- including down the 
> paths that throw
>   (the CAMEL-24754 lesson).
> * *A missing identity is a deny, not an evaluation failure.* If the 
> configured {{user}} or {{object}}
>   expression resolves to blank, the exchange is denied and {{failOpen}} does 
> *not* apply: nothing
>   failed at the decision point, the request simply carried no identity. 
> Without this split,
>   {{failOpen}} would turn "no identity" into "allowed".
> * *A wildcard subject is rejected.* Verified against OpenFGA v1.21.0: 
> {{check(user:*, reader,
>   document:public)}} returns {{allowed: true}} whenever a public-read tuple 
> exists. {{user:*}} is a
>   legitimate *tuple* subject ("public") but never a legitimate *checking* 
> subject, so a {{user}}
>   expression resolving to a typed wildcard is denied rather than sent.
> * *No contextual tuples or condition context in this first cut.* A contextual 
> tuple derived from a
>   message is a self-authorization primitive ({{(user:me, owner, 
> document:secret)}}), so it is left
>   out entirely rather than shipped with a gate that has not been reviewed. 
> Follow-up issue.
> * Secrets ({{apiToken}}, {{clientSecret}}) are marked {{secret = true}}; both 
> already match
>   {{SensitiveUtils}} keywords, so endpoint URIs are masked in logs, JMX and 
> health output.
> * {{sslContextParameters}} / {{useGlobalSslContextParameters}} for TLS, 
> including presenting a client
>   certificate to an OpenFGA server that requires mutual TLS -- a SPIFFE 
> X.509-SVID, closing the loop
>   with {{camel-spiffe}}.
> h2. Deliverables
> * {{components/camel-openfga}} -- component, endpoint, producer, 
> configuration, constants, operations,
>   {{security/OpenFgaSecurityPolicy}}.
> * Producer and security-policy readiness health checks probing the server's 
> {{/healthz}} endpoint
>   (verified to return {{{"status":"SERVING"}}} with HTTP 200; the probe 
> asserts the body, not only the
>   status, so a server answering 200 while not serving is not reported UP).
> * {{test-infra/camel-test-infra-openfga}} -- Testcontainers service on
>   {{mirror.gcr.io/openfga/openfga}}. The image publishes {{amd64}} and 
> {{arm64}} only, so
>   {{skipITs.ppc64le}} and {{skipITs.s390x}} are set, as for {{camel-opa}}.
> * Unit tests (mocked client) plus an end-to-end IT against a real OpenFGA 
> server.
> * Component documentation.
> h2. Dependency
> {{dev.openfga:openfga-sdk}} 0.10.1 (Apache-2.0). Transitively Jackson 2 
> (already managed by Camel's
> parent), {{org.openapitools:jackson-databind-nullable}} and 
> {{io.opentelemetry:opentelemetry-api}}.
> Unlike the OPA SDK, it applies default connect/read timeouts (10s each), 3 
> retries, and reuses a
> single {{HttpClient}}, so the component does not need to supply its own 
> transport -- only to expose
> the knobs and to bound the blocking wait so a routing thread can never park 
> indefinitely.
> h2. Follow-ups (separate issues once this lands)
> * Contextual tuples and condition context on {{check}}, with an explicit 
> opt-in and a reviewed trust model.
> * {{expand}}, {{readTuples}} and {{readChanges}} operations.
> * Store and authorization-model management operations.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to