QONTO-WP-0004-T04. Modeled directly on examples/tenant-engine/: one resource type (finance-snapshot), one action (finance.qonto.read), two registered subjects (an agent-harness session identity and the founder's human identity), and a Rego policy gating on resource.system + action + subject.type + tenant match. Tenant capability-role/plan liveness (VEN/CUS) is deliberately NOT encoded here -- that's qonto-assistant's separate tenant-engine live-lookup check, per this package's own scope note. Verified: flex-auth test-policy (6 rego tests + 6 fixtures, all pass), load-registry, and CLI check for both an allow and a deny case. Also verified end-to-end over real HTTP: a live flex-auth serve loaded with this exact registry+policy, hit by qonto-assistant's actual FlexAuthCheckClient (not a mock) -- allow for tenant:friendly:binky, deny (wrong_tenant) for a mismatched tenant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
167 lines
5 KiB
Markdown
167 lines
5 KiB
Markdown
---
|
|
id: qonto-assistant.finance-read
|
|
name: qonto-assistant finance.qonto.read authorization
|
|
namespace: qonto-assistant:finance
|
|
version: v1
|
|
status: ready
|
|
package: flexauth.qonto_assistant.finance_read
|
|
actions:
|
|
- finance.qonto.read
|
|
owner: team:platform-security
|
|
fixtures:
|
|
- policy_fixtures.yaml
|
|
caring:
|
|
profile: caring-0.4.0-rc2
|
|
enforce: false
|
|
canonical_roles:
|
|
- Operator
|
|
organization_relations:
|
|
- ServiceProvider
|
|
- Customer
|
|
scopes:
|
|
- level: Tenant
|
|
id: tenant:friendly:binky
|
|
tenant: tenant:friendly:binky
|
|
planes:
|
|
- Data
|
|
- Audit
|
|
capabilities:
|
|
- View
|
|
- Audit
|
|
exposure_modes:
|
|
- Masked
|
|
conditions:
|
|
- Logged
|
|
restrictions:
|
|
- PrivilegeEscalationBlocked
|
|
activation:
|
|
mode: local
|
|
metadata:
|
|
source: examples/qonto-assistant/policy_package.md
|
|
flex_auth_contract: protected-system-v0
|
|
---
|
|
|
|
# qonto-assistant finance.qonto.read authorization
|
|
|
|
This package authorizes `qonto-assistant`'s read surface
|
|
(`QONTO-WP-0004-T04`'s live authorization gate). `qonto-assistant` keeps
|
|
custody of the bank credential and its own default-deny policy kernel
|
|
(spend/transfer/card/write tools are hard-denied there and never reach this
|
|
policy); flex-auth decides whether a specific *actor* may use the
|
|
`finance.qonto.read` capability at all.
|
|
|
|
**Scope note:** this policy governs *who may call `finance.qonto.read`* (an
|
|
actor/tenant question) — it does not evaluate a *tenant's* capability roles
|
|
or plan status (`PLTF`/`IAM`/`VEN`/`CUS`, ADR-0014). Those are tenant state
|
|
`qonto-assistant` checks separately via `tenant-engine`'s live-lookup
|
|
endpoint (`GET /tenants/{id}/roles/live`); conflating the two here would
|
|
authorize the wrong thing, exactly as tenant-engine's own
|
|
`policy_package.md` notes for its analogous case.
|
|
|
|
Single-tenant dogfood today (`tenant:friendly:binky` only); generalizing to
|
|
other tenants is a policy update here, not a `qonto-assistant` code change.
|
|
|
|
## Rules
|
|
|
|
```rego
|
|
import future.keywords.contains
|
|
import future.keywords.if
|
|
import future.keywords.in
|
|
|
|
valid_actions := {"finance.qonto.read"}
|
|
|
|
valid_subject_types := {"agent", "human", "service"}
|
|
|
|
known_tenant := "tenant:friendly:binky"
|
|
|
|
decision := {"effect": "allow", "reason": "finance_read_policy_matched"} if {
|
|
allowed
|
|
} else := {"effect": "deny", "reason": first_denial} if {
|
|
true
|
|
}
|
|
|
|
allowed if {
|
|
input.resource.system == "qonto-assistant"
|
|
input.action in valid_actions
|
|
input.subject.type in valid_subject_types
|
|
input.tenant == known_tenant
|
|
}
|
|
|
|
default first_denial := "no_matching_rule"
|
|
|
|
first_denial := "wrong_system" if {
|
|
input.resource.system != "qonto-assistant"
|
|
} else := "unknown_action" if {
|
|
not input.action in valid_actions
|
|
} else := "wrong_subject_type" if {
|
|
not input.subject.type in valid_subject_types
|
|
} else := "wrong_tenant" if {
|
|
input.tenant != known_tenant
|
|
}
|
|
```
|
|
|
|
## Tests
|
|
|
|
```rego test
|
|
package flexauth.qonto_assistant.finance_read_test
|
|
|
|
import future.keywords.if
|
|
import data.flexauth.qonto_assistant.finance_read
|
|
|
|
base_request := {
|
|
"id": "check:qonto-assistant-read",
|
|
"tenant": "tenant:friendly:binky",
|
|
"subject": {"id": "agent-harness-binky", "type": "agent"},
|
|
"action": "finance.qonto.read",
|
|
"resource": {"id": "finance-snapshot", "type": "finance-snapshot", "system": "qonto-assistant"}
|
|
}
|
|
|
|
test_agent_read_allowed if {
|
|
finance_read.decision.effect == "allow" with input as base_request
|
|
}
|
|
|
|
test_human_read_allowed if {
|
|
finance_read.decision.effect == "allow" with input as {
|
|
"tenant": "tenant:friendly:binky",
|
|
"subject": {"id": "bernd.worsch", "type": "human"},
|
|
"action": "finance.qonto.read",
|
|
"resource": {"id": "finance-snapshot", "type": "finance-snapshot", "system": "qonto-assistant"}
|
|
}
|
|
}
|
|
|
|
test_wrong_system_denied if {
|
|
finance_read.decision.reason == "wrong_system" with input as {
|
|
"tenant": "tenant:friendly:binky",
|
|
"subject": {"id": "agent-harness-binky", "type": "agent"},
|
|
"action": "finance.qonto.read",
|
|
"resource": {"id": "x", "type": "finance-snapshot", "system": "some-other-system"}
|
|
}
|
|
}
|
|
|
|
test_unknown_action_denied if {
|
|
finance_read.decision.reason == "unknown_action" with input as {
|
|
"tenant": "tenant:friendly:binky",
|
|
"subject": {"id": "agent-harness-binky", "type": "agent"},
|
|
"action": "finance.qonto.transfer",
|
|
"resource": {"id": "finance-snapshot", "type": "finance-snapshot", "system": "qonto-assistant"}
|
|
}
|
|
}
|
|
|
|
test_wrong_subject_type_denied if {
|
|
finance_read.decision.reason == "wrong_subject_type" with input as {
|
|
"tenant": "tenant:friendly:binky",
|
|
"subject": {"id": "unknown-device", "type": "device"},
|
|
"action": "finance.qonto.read",
|
|
"resource": {"id": "finance-snapshot", "type": "finance-snapshot", "system": "qonto-assistant"}
|
|
}
|
|
}
|
|
|
|
test_wrong_tenant_denied if {
|
|
finance_read.decision.reason == "wrong_tenant" with input as {
|
|
"tenant": "tenant:friendly:some-other-company",
|
|
"subject": {"id": "agent-harness-binky", "type": "agent"},
|
|
"action": "finance.qonto.read",
|
|
"resource": {"id": "finance-snapshot", "type": "finance-snapshot", "system": "qonto-assistant"}
|
|
}
|
|
}
|
|
```
|