Open
Conversation
The Cedar authorizer hardcoded EntityTypeTHVGroup ("THVGroup") as the
entity type used to construct Client.Parents UIDs from JWT group/role
claims. Deployments whose policy vocabulary doesn't use "THVGroup"
had no way to switch without patching the authorizer.
Add an optional GroupEntityType field to ConfigOptions and thread it
through EntityFactory so deployments can use names like OrgRole or
Group that fit their policy vocabulary. Empty defaults to "THVGroup"
so existing deployments are unaffected.
Validate non-empty values up front in NewCedarAuthorizer, per the
constructor-validation rule in .claude/rules/go-style.md, so a typo
fails at startup with a message naming the offending input rather
than producing broken entity lookups at request time. Delegate the
identifier-grammar check to cedar-go's policy parser by round-tripping
a synthetic permit() against the configured type — Cedar's parser is
the source of truth for what a valid identifier looks like, and any
future grammar refinements upstream land here automatically. Reject
the "::" namespace separator ourselves: namespaced types are out of
scope until parseCedarEntityID handles them.
Warn at construction when entities_json contains entities of type
"THVGroup" while GroupEntityType is configured to a different value.
The mismatch causes Cedar's "in" operator to evaluate to false on
those stale entities — a silent deny that is hard to debug without
this diagnostic.
Closes #5072
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5121 +/- ##
=======================================
Coverage 67.12% 67.13%
=======================================
Files 597 597
Lines 60170 60192 +22
=======================================
+ Hits 40390 40408 +18
- Misses 16706 16712 +6
+ Partials 3074 3072 -2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
EntityTypeTHVGroup = "THVGroup"as the entity type used to construct principalParentsUIDs from JWT group/role claims. Deployments whose policy vocabulary doesn't use "THVGroup" had no way to switch without patching the authorizer.GroupEntityTypefield tocedar.ConfigOptionsso deployments can use names likeOrgRoleorGroupthat fit their policy vocabulary. Empty defaults to"THVGroup", so existing deployments are unaffected.NewCedarAuthorizerby round-tripping through cedar-go's policy parser. This delegates Cedar's identifier grammar to upstream — reserved words, ANYIDENT shape, the__cedartoken — so future cedar-go refinements land automatically. The one project-specific rule we keep is rejecting the::namespace separator (namespaced types are out of scope untilparseCedarEntityIDhandles them).entities_jsoncontains stale entities of type"THVGroup"whileGroupEntityTypeis set to a different value. The mismatch causes Cedar'sinoperator to silently evaluate to false on those entities — a silent deny that is otherwise hard to debug.Closes #5072
Type of change
Test plan
task test)task lint-fix)No e2e tests added — the new field is consumed today only via the standalone CLI's
--authz-config <file>path. The operator and vMCP constructcedar.ConfigOptionsby hand and don't yet surface the field; that's tracked as a separate follow-up.API Compatibility
v1beta1API. The new field lives on the inline CedarConfigOptionsstruct, which CRD consumers pass through as opaque YAML/JSON — no CRD schema change.Changes
pkg/authz/authorizers/cedar/core.goGroupEntityTypefield toConfigOptions; addvalidateGroupEntityTypehelper that delegates to cedar-go's policy parser; wire validation + factory construction intoNewCedarAuthorizer; emit silent-deny warn diagnosticpkg/authz/authorizers/cedar/entity.goEntityFactorystateful with agroupEntityTypefield; changeNewEntityFactorysignature to accept the type (empty defaults toEntityTypeTHVGroup); replace the hardcoded constant inCreateEntitiesForRequestwith the configured valuepkg/authz/authorizers/cedar/core_test.goTHVGroupwarn diagnostic testpkg/authz/authorizers/cedar/entity_test.goNewEntityFactorysignatureDoes this introduce a user-facing change?
Yes. Users with a YAML/JSON authz config (
thv run --authz-config <file>) can now set an optionalgroup_entity_type:field to any valid Cedar identifier. Empty (or absent) keeps the existing"THVGroup"behaviour. Invalid values fail at startup with a message naming the offending input — namespace separators (::), Cedar reserved words, and identifier-grammar violations are all rejected.Implementation plan
Approved implementation plan
Context
pkg/authz/authorizers/cedar/entity.gohardcodedEntityTypeTHVGroup cedar.EntityType = "THVGroup".EntityFactory.CreateEntitiesForRequestused it to buildClient.ParentsUIDs from JWT group/role claims. Existing deployments couldn't rename the principal-hierarchy entity type without a code change.Approach
Add an optional
GroupEntityTypefield toConfigOptions(json:"group_entity_type,omitempty"). MakeEntityFactorystateful so the configured type is resolved once at construction. KeepEntityTypeTHVGroupas the default constant and fallback. The wire type staysstring;NewEntityFactorytakes acedar.EntityTypeso callers cast at the boundary.Validate non-empty input in
NewCedarAuthorizerbefore allocation. Originally the validator hand-rolled the rules (regex + reserved-word map +__cedarprefix check); the final form delegates tocedar.Policy.UnmarshalCedaragainst a syntheticpermit(principal in <TYPE>::"x", action, resource);policy. Reasoning:::namespace rejection stays as a project-specific rule (issue text defers namespaced types).Add a startup
WARNlog whenentities_jsoncontains entities of type"THVGroup"whileGroupEntityTypeis configured to a different non-empty value. Cedar'sinwould silently evaluate to false on those stale entities; the diagnostic surfaces the mismatch so operators don't spend hours debugging a silent deny. Scoped to entities_json — policy-text scanning was rejected as fragile.Out of scope
Foo::Bar). Blocked byparseCedarEntityID. Tracked for follow-up.GroupEntityType(and the existingGroupClaimName/RoleClaimName) through the operator'sAuthzConfigRef.Inlineand vMCP'sAuthzConfigboundaries. Both layers currently constructcedar.ConfigOptionsby hand and forward only a subset of fields. Tracked for a separate PR.Files modified
pkg/authz/authorizers/cedar/core.go(production)pkg/authz/authorizers/cedar/entity.go(production)pkg/authz/authorizers/cedar/core_test.go(test)pkg/authz/authorizers/cedar/entity_test.go(test)Special notes for reviewers
permit()policy throughcedar.Policy.UnmarshalCedar. That makes cedar-go the source of truth for "is this a valid Cedar identifier" and means we don't have to track upstream grammar changes. The one project-specific rule we keep is rejecting::. The behavioural consequence vs a strict hand-rolled version is that__cedarFoois now accepted — the Cedar spec only reserves the literal__cedartoken, not the entire prefix namespace.entities_jsononly. Policy-text scanning was considered and rejected — false positives during phased migrations are likely, and the policy parser has no schema-aware type check. The entities-json scan is deterministic (entity types in JSON are explicit) and runs once at startup.TestValidateGroupEntityTypedeliberately covers our package's branches (empty short-circuit,::rejection, valid/invalid smoke tests, the__cedarFooacceptance) rather than enumerating every input cedar-go's parser would reject. The exhaustive grammar coverage lives in cedar-go's test suite — re-asserting it here would test a dependency, not our code.🤖 Generated with Claude Code