* fix(identity): route the remaining permission checks through the evaluator
Completes T038 of the authorization model, and fixes a live defect it was
meant to catch.
RoleDeletionCoordinator.InspectAsync gated on the legacy string "delete:role",
compared by claim-value equality. Nothing has granted that spelling since the
vocabulary migration, so a caller holding identity/roles:delete passed the
endpoint's own RequirePermission check and was then refused mid-handler by the
coordinator. In practice role deletion worked only for holders of "*", across
all three routes that reach the coordinator (Delete, RemediateAndDelete and
GetDeletionImpact). The check now evaluates identity/roles:delete through
PermissionEvaluator, so structured and wildcard grants both reach it.
Every existing coordinator test acted as an administrator holding "*", which is
why this went unnoticed; the added cases exercise identity/roles:delete,
identity/*:delete and identity/roles:* and assert an unrelated grant is still
refused.
AIHttpContextIdentity matched an agent's required permissions by
case-insensitive exact-set containment, which both admitted casing the rest of
the model rejects and refused the wildcards it honours. It now evaluates each
required permission through the same evaluator.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(ai): restore the null-safe HttpContext access in the tools endpoint
The tools endpoint dereferenced HttpContext directly when passing the principal
to GetAuthorizedAgent, which threw a NullReferenceException and failed two
Elsa.AI.IntegrationTests cases on CI.
This was collateral from tightening a nullable warning in the chat endpoint. The
chat endpoint dereferences HttpContext.Response unconditionally a few lines
later, so HttpContext.User is safe there; the tools endpoint never does, and its
three sibling calls -- GetPermissions, GetActorId and GetTenantId -- all accept
a null context. The same edit was applied to both, and only chat could take it.
Reverting to HttpContext?.User preserves the endpoint's prior behaviour:
GetAuthorizedAgent treats a null principal as holding nothing, and an agent that
declares no required permissions stays authorized either way, because the
empty-requirements check runs before the null check.
Elsa.AI.IntegrationTests 69/69 (was 67 passed, 2 failed); Elsa.Identity.UnitTests
115/115; Elsa.AI.Host builds with no CS8602.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>