Chapter 06 / 09
ENSK

SmartExchange · .NET 10 · Chapter 06

PlanResources → AST → SQL

The shared client returns a neutral query plan. The SQL adapter translates only supported expressions and known mappings; Cerbos never returns executable SQL.

Shared code · Intaker + Web APIReviewed · 2026-10-27

Transport and neutral contracts

Class / methodImplemented responsibility
CerbosAuthorizationPolicyClient.PlanResourcesAsync()Maps principal/resource attributes to Protobuf, sends one action over gRPC, includes metadata and propagates deadline/cancellation. Reuse the client/channel for the application lifetime; dispose at shutdown.
CerbosQueryPlanMapper.Map(response, request)Checks validation errors and request/resource/action identity. Maps the filter to AuthorizationQueryPlan, preserving DecisionId.
AuthorizationQueryPlanKind, ResourceKind, Action, RequestId, DecisionId, optional Filter.
Expression recordsAuthorizationAttribute, AuthorizationConstant, AuthorizationOperation, AuthorizationCollectionExists.

The mapper supports and, or, eq, in, constant lists and a resource collection’s single-variable exists/lambda. This is a deliberately limited translator, not all of CEL. Unsupported forms throw; authorization is not silently skipped.

Shared SQL boundary

Interface and factory · signatures condensed
// BmcGroup.Authorization.SqlKata
Query IAuthorizationQueryAdapter.Apply(
    Query query, AuthorizationQueryPlan plan, AuthorizationSqlMapping mapping);

AuthorizationSqlMapping AuthorizationSqlMappingFactory.Create(
    string resourceKind, string action, AuthorizationSqlAttribute resourceId,
    IEnumerable<AuthorizationRelationship> relationships);

AuthorizationSqlMapping binds resource/action, scalar attributes and relationships. AuthorizationSqlAttribute supplies a trusted column and value type; AuthorizationSqlRelationship supplies an element column and a fresh correlated query through CreateQuery.

Plan kindSqlKataAuthorizationQueryAdapter.Apply()
AlwaysAllowedKeep the business query.
AlwaysDeniedAdd 1 = 0. Intaker rejects it earlier.
ConditionalBuild a separate predicate, then AND it into the query.

The adapter checks matching resource/action, typed constants and mapped identifiers. Current guards include depth 32 and at most 1,800 authorization parameters. A rejected plan must terminate the operation.

Both exports through the same AST and SQL adapter

PlanResourcesAsync() → CerbosQueryPlanMapper.Map() → AuthorizationCollectionExists → AuthorizationSqlMappingFactory.Create() → SqlKataAuthorizationQueryAdapter.Apply().

Person/export

resource.attr.companyIds → AuthorizationCollectionExists → EXISTS PersonInCompany JOIN Company.

PlanResources request · JSON
PlanResources request · JSON
{
  "requestId": "presentation-person-export",
  "actions": [
    "export"
  ],
  "includeMeta": true,
  "principal": {
    "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "roles": [
      "user"
    ],
    "attr": {
      "product": "QA",
      "grants": [
        {
          "assignmentId": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
          "resourceId": "11111111-1111-1111-1111-111111111111",
          "resource": "Company",
          "action": "read",
          "scope": "Resource",
          "isPublic": false
        }
      ]
    }
  },
  "resource": {
    "kind": "Person",
    "policyVersion": "presentation-demo",
    "attr": {}
  }
}
Expected plan shape · illustrative JSON, not captured
Expected plan shape · illustrative JSON, not captured
{
  "requestId": "presentation-person-export",
  "actions": [
    "export"
  ],
  "resourceKind": "Person",
  "policyVersion": "presentation-demo",
  "filter": {
    "kind": "KIND_CONDITIONAL",
    "condition": {
      "expression": {
        "operator": "exists",
        "operands": [
          {
            "variable": "request.resource.attr.companyIds"
          },
          {
            "expression": {
              "operator": "lambda",
              "operands": [
                {
                  "expression": {
                    "operator": "eq",
                    "operands": [
                      {
                        "variable": "relatedId"
                      },
                      {
                        "value": "11111111-1111-1111-1111-111111111111"
                      }
                    ]
                  }
                },
                {
                  "variable": "relatedId"
                }
              ]
            }
          }
        ]
      }
    }
  }
}
SQL · equivalent query shape
SQL · equivalent query shape
-- Synthetic Person/Company demonstration; equivalent SQL, not a captured query.
-- Read-only example. No policy or ACL assignments are changed.
DECLARE @companyId uniqueidentifier = '11111111-1111-1111-1111-111111111111';

SELECT [Person].*
FROM dbo.Person AS [Person]
WHERE [Person].[Deleted] = 0
  AND EXISTS (
      SELECT 1
      FROM dbo.PersonInCompany AS acl_relation_0_0
      INNER JOIN dbo.Company AS acl_relation_0_1
        ON acl_relation_0_1.Id = acl_relation_0_0.CompanyId
       AND acl_relation_0_1.Deleted = 0
      WHERE acl_relation_0_0.PersonId = [Person].Id
        AND acl_relation_0_0.Deleted = 0
        AND acl_relation_0_1.Id = @companyId
  );

-- Production code binds @companyId as a GUID through SqlKata.
-- Its actual parameter names may differ. Keep template/instance filters.
-- Authorization is applied before paging/counts and before returning data.

Request · Expected plan · SQL

InterestedParty/export

resource.attr.projectIds → AuthorizationCollectionExists → EXISTS Project.

PlanResources request · JSON
PlanResources request · JSON
{
  "requestId": "presentation-interested-party-export",
  "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": "presentation-demo",
    "attr": {}
  },
  "actions": [
    "export"
  ],
  "includeMeta": true
}
Expected plan shape · illustrative JSON, not captured
Expected plan shape · illustrative JSON, not captured
{
  "requestId": "presentation-interested-party-export",
  "actions": [
    "export"
  ],
  "resourceKind": "InterestedParty",
  "policyVersion": "presentation-demo",
  "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"
                }
              ]
            }
          }
        ]
      }
    }
  }
}
SQL · equivalent query shape
SQL · equivalent query shape
-- Proposed InterestedParty/export Project-scope example; equivalent SQL, not captured runtime output.
-- 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.

Request · Expected plan · SQL

Both documentation requests select presentation-demo/export and keep record-specific relationship attributes unknown. Both expected JSON examples show a complete EXISTS/lambda predicate tree for one qualifying ID; they are illustrative and were not obtained from a live call. SQL is likewise an equivalent shape, not captured execution output.

Correlated EXISTS avoids multiplying root rows. Deleted=0 is enforced for every relationship step and the root business query. Apply the authorization predicate before count/paging/materialization.

Separate runtime evidence: actual InterestedParty/read JSON AST.