SmartExchange · .NET 10 · Chapter 09
InterestedParty read and Project Scope
Kompletný migračný príklad: InterestedPartyRead pridelené na Project sa zmení na InterestedParty/read so scope daného Project. Normalizácia a validácia sú v kóde; zodpovedajúca read policy a derived role sú nasadené.
Oba export príklady: celý postup
| Krok | Person export | InterestedParty export |
|---|---|---|
| Rola / assignment | CompanyReader → Company-A | Project View / InterestedPartyRead → Project |
| Normalizovaný grant | Company/read/Resource | InterestedParty/read/Project |
| Výber principala | Person + Company | InterestedParty + Project + Investor |
| Derived role | person_with_readable_company | interested_party_in_project_scope |
| Celá demo policy | Person / presentation-demo / export | InterestedParty / presentation-demo / export |
| Plan / SQL | Request · Expected AST · SQL EXISTS Company | Request · Expected AST · SQL EXISTS Project |
Obe kompletné derived roles/policies sú vedľa seba v kapitole 05; requests, očakávané plans a SQL sú spárované v kapitole 06. Oba export príklady sú dokumentačné návrhy. Nasledujúca migračná referencia zachováva samostatne overené InterestedParty/read správanie, skutočný zachytený AST a históriu nasadenia.
1. Assignment → štandardný grant
Uložený DEV snapshot (2026-09-17) obsahuje rolu Project View, permission InterestedPartyRead a assignment a7f53aab-6f40-4204-b1d3-02ea54f1713e na Project b48e464a-88cf-4743-bbce-98d6ba89e399 pre usera 5a3e5b0f-fb6d-4abf-ba4f-8b581365a795.
{
"assignmentId": "a7f53aab-6f40-4204-b1d3-02ea54f1713e",
"resourceId": "b48e464a-88cf-4743-bbce-98d6ba89e399",
"resource": "InterestedParty",
"action": "read",
"scope": "Project",
"isPublic": false
}Permission určí resource=InterestedParty a action=read. Miesto assignmentu sa zmení na scope=Project; resourceId je ID tohto Project, nie InterestedParty. CRM permission metadáta a deklarovaný vzťah InterestedParty→Project umožnia generickú konverziu.
SmartExchangeAuthorizationScopeValidator.Validate() overí dvojicu aj ID. Nepridávame atribút pre výnimku. Názov roly neurčuje resource a bežné Project/read neudeľuje InterestedParty/read.
2. Resolver → planning request
SmartExchangeAuthorizationContextResolver.ResolveAsync() vyberie InterestedParty, Project a Investor grant typy. Izolovaný príklad odošle iba grant vyššie, roles: ["user"] a attr.product. Reálny context môže obsahovať ďalšie vybrané grants; nejde o kompletný aktuálny prístup daného usera.
Celý PlanResources request · izolovaný príklad
{
"requestId": "interested-party-project-scope-plan",
"principal": {
"id": "5a3e5b0f-fb6d-4abf-ba4f-8b581365a795",
"roles": [
"user"
],
"attr": {
"product": "API_Dev1",
"grants": [
{
"assignmentId": "a7f53aab-6f40-4204-b1d3-02ea54f1713e",
"resourceId": "b48e464a-88cf-4743-bbce-98d6ba89e399",
"resource": "InterestedParty",
"action": "read",
"scope": "Project",
"isPublic": false
}
]
}
},
"resource": {
"kind": "InterestedParty",
"policyVersion": "default",
"attr": {}
},
"actions": [
"read"
],
"includeMeta": true
}AuthorizationPlanRequest používa resource InterestedParty a action read. Pri plánovaní listu zostáva projectIds konkrétneho záznamu neznáme; request nevymenúva InterestedParty ID.
3. Nasadená derived role + resource policy
apiVersion: api.cerbos.dev/v1
description: InterestedParty read restricted by an explicit Project scope.
derivedRoles:
name: interested_party_project_scope
variables:
local:
projectScopeIds: >-
has(P.attr.grants) ? P.attr.grants.filter(g,
g.resource == "InterestedParty" &&
g.action == "read" &&
g.scope == "Project" &&
g.resourceId != "00000000-0000-0000-0000-000000000000"
).map(g, g.resourceId) : []
definitions:
- name: interested_party_in_project_scope
parentRoles: [user]
condition:
match:
expr: >-
R.kind == "InterestedParty" &&
size(V.projectScopeIds) > 0 &&
R.attr.projectIds.exists(projectId,
projectId in V.projectScopeIds)# Extract from live InterestedParty/default.
# Existing export and other read rules are omitted here.
resourcePolicy:
version: default
resource: InterestedParty
importDerivedRoles:
- interested_party_project_scope
rules:
- name: read-in-project-scope
actions: [read]
derivedRoles: [interested_party_in_project_scope]
effect: EFFECT_ALLOWRola vyberá ID z InterestedParty/read/Project grants a porovná ich s projectIds záznamu. Nevyberá Project/read grants. Resource policy importuje set a explicitne povoľuje read cez túto rolu. Ďalšie runtime read pravidlá pokrývajú priamy Resource scope a Instance scope.
Celá runtime policy vrátane zachovaných export pravidiel · Runtime derived-role YAML
4. Skutočná Cerbos odpoveď → SQL
Odpoveď nižšie bola zachytená 2026-10-26 pre overovací request verify-project-scope-loaded-20261026. Používa rovnaký Project scope fakt; ide o samostatné volanie, nie o odpoveď na ilustračný request vyššie. AST je kompletný, nezjednodušený.
Skutočná PlanResources odpoveď · celý JSON AST
{
"requestId": "verify-project-scope-loaded-20261026",
"actions": [
"read"
],
"resourceKind": "InterestedParty",
"policyVersion": "default",
"filter": {
"kind": "KIND_CONDITIONAL",
"condition": {
"expression": {
"operator": "exists",
"operands": [
{
"variable": "request.resource.attr.projectIds"
},
{
"expression": {
"operator": "lambda",
"operands": [
{
"expression": {
"operator": "eq",
"operands": [
{
"variable": "projectId"
},
{
"value": "b48e464a-88cf-4743-bbce-98d6ba89e399"
}
]
}
},
{
"variable": "projectId"
}
]
}
}
]
}
}
},
"meta": {
"filterDebug": "(exists request.resource.attr.projectIds (lambda (eq projectId \"b48e464a-88cf-4743-bbce-98d6ba89e399\") projectId))",
"matchedScopes": {
"read": ""
}
},
"cerbosCallId": "01M5W4E4R3TT90WTC83BBFBQCG"
}CerbosQueryPlanMapper.Map() zmení exists/lambda/eq na AuthorizationCollectionExists a porovnanie. Registry mapuje resource.attr.projectIds na InterestedParty→Project; SqlKataAuthorizationQueryAdapter.Apply() vie podporovaný tvar preložiť na EXISTS.
-- Target SQL shape for the isolated InterestedParty/read Project-scope example.
-- Illustration, not captured runtime output. Bind @ProjectId from the approved plan.
-- Every schema/table/column mapping comes from trusted application metadata.
DECLARE @ProjectId uniqueidentifier = 'b48e464a-88cf-4743-bbce-98d6ba89e399';
SELECT ip.Id
FROM dbo.InterestedParty AS ip
WHERE ip.Deleted = 0
AND EXISTS (
SELECT 1
FROM dbo.Project AS acl_project
WHERE acl_project.Id = ip.ProjectId
AND acl_project.Deleted = 0
AND acl_project.Id = @ProjectId
);
-- Ordinary business filters are ANDed with this predicate.
-- Apply authorization before COUNT, ORDER BY/OFFSET and export materialization.
-- No user-supplied ProjectId filter is required to activate this authorization.Spoločné prekladové komponenty už existujú. Reálne presadenie v query Web API a adapter→Cerbos→databáza test tohto prípadu ešte chýbajú.
5. Overený výsledok a zostávajúca integrácia
| Krok | Stav |
|---|---|
| Generická normalizácia + validácia scope | Implementované v kóde; pri tejto dokumentačnej úprave neprebehol C# build/test. |
| Policy a derived role v non-prod | Nasadené 2026-10-26; pripojené YAML znovu skompilované 2026-10-27. |
| Správanie policy | 27 API scenárov / 51 record rozhodnutí overených 2026-10-26. Zhodný project ALLOW; iný/prázdny project DENY; bežné Project/read DENY. |
| Web API a CheckResources integrácia | Chýba spoločná klientská metóda/resource-attribute provider a zapojenie endpointov. |
Pri overení konkrétneho záznamu musí aplikácia načítať dôveryhodné projectIds. Pri list plánovaní dodá vzťah SQL adapter cez query. IQ500, portal aj PmTask pravidlá musia používať rovnaký resource/action/scope model; špeciálne správanie PmTask ešte vyžaduje analýzu kódu.
Plan request · Zachytená odpoveď · CheckResources request · SQL