Part of stacklok/stacklok-enterprise-platform#stacklok/stacklok-enterprise-platform#376
Description
Store the serverName parameter on the Cedar Authorizer struct and update the NewCedarAuthorizer constructor to accept it. The AuthorizerFactory.CreateAuthorizer interface already passes serverName to the Cedar factory, but the Cedar implementation discards it with _ string. This task makes the authorizer retain the value so that downstream changes (server-scoped resource entities, MCP-based list operations, group extraction) can use it.
This is a prerequisite for server-scoped authorization in the enterprise platform. Without serverName stored on the authorizer, compiled Cedar policies that use resource in MCP::"<server>" cannot be evaluated correctly.
Context
The Platform Authorization design extends the existing cedarv1 authorizer with optional enterprise capabilities. Several downstream OSS changes (#4768, #4769, #4770) depend on serverName being available on the Authorizer struct. Currently the factory discards it, so the struct has no way to reference the server identity at authorization time.
The change is backward-compatible: when serverName is empty (standalone Cedar usage with no enterprise controller), the authorizer behaves identically to today.
Dependencies: None (can start immediately)
Blocks: #4768 (group/role extraction), #4769 (MCP parent on resources), #4770 (FeatureType to MCP for list ops)
Acceptance Criteria
Technical Approach
Recommended Implementation
Three changes in pkg/authz/authorizers/cedar/core.go:
- Add
serverName field to Authorizer struct (line 116):
type Authorizer struct {
policySet *cedar.PolicySet
entities cedar.EntityMap
entityFactory *EntityFactory
serverName string // NEW: server identity for scoped policies
mu sync.RWMutex
}
- Update
NewCedarAuthorizer signature (line 137):
// Current
func NewCedarAuthorizer(options ConfigOptions) (authorizers.Authorizer, error)
// Proposed
func NewCedarAuthorizer(options ConfigOptions, serverName string) (authorizers.Authorizer, error)
Store serverName in the Authorizer struct during construction:
func NewCedarAuthorizer(options ConfigOptions, serverName string) (authorizers.Authorizer, error) {
authorizer := &Authorizer{
policySet: cedar.NewPolicySet(),
entities: cedar.EntityMap{},
entityFactory: NewEntityFactory(),
serverName: serverName,
}
// ... rest unchanged ...
}
- Update
CreateAuthorizer factory (line 88):
// Current
func (*Factory) CreateAuthorizer(rawConfig json.RawMessage, _ string) (authorizers.Authorizer, error) {
// ...
return NewCedarAuthorizer(*config.Options)
}
// Proposed
func (*Factory) CreateAuthorizer(rawConfig json.RawMessage, serverName string) (authorizers.Authorizer, error) {
// ...
return NewCedarAuthorizer(*config.Options, serverName)
}
- Mechanical test updates: Add
"" as the second argument to all 19 NewCedarAuthorizer(...) call sites in test files. The empty string preserves identical behavior for every existing test.
Patterns & Frameworks
- Follow existing constructor pattern in
core.go (functional struct initialization)
- Table-driven tests with
testify assertions per project conventions
t.Parallel() on tests with no shared mutable state
Code Pointers
pkg/authz/authorizers/cedar/core.go lines 86-99 -- CreateAuthorizer factory method; currently discards serverName with _ string; needs to accept and pass through
pkg/authz/authorizers/cedar/core.go lines 115-125 -- Authorizer struct definition; needs new serverName field
pkg/authz/authorizers/cedar/core.go lines 136-167 -- NewCedarAuthorizer constructor; needs serverName parameter and storage
pkg/authz/authorizers/registry.go lines 16-24 -- AuthorizerFactory interface definition; already passes serverName to CreateAuthorizer
pkg/authz/authorizers/cedar/core_test.go -- 10 call sites to update (lines 74, 316, 341, 652, 713, 764, 804, 828, 917 use NewCedarAuthorizer(ConfigOptions{...}))
pkg/authz/middleware_test.go -- 4 call sites (lines 33, 417, 783, 953)
pkg/authz/response_filter_test.go -- 3 call sites (lines 26, 221, 270)
pkg/authz/integration_test.go -- 2 call sites (lines 30, 332)
Component Interfaces
The function signature change:
// Before
func NewCedarAuthorizer(options ConfigOptions) (authorizers.Authorizer, error)
// After
func NewCedarAuthorizer(options ConfigOptions, serverName string) (authorizers.Authorizer, error)
The factory signature is unchanged (it already accepts serverName):
// AuthorizerFactory interface (unchanged)
CreateAuthorizer(rawConfig json.RawMessage, serverName string) (Authorizer, error)
Testing Strategy
Unit Tests
Existing Test Validation
Edge Cases
Out of Scope
References
pkg/authz/authorizers/registry.go -- AuthorizerFactory interface already defines serverName parameter
Part of stacklok/stacklok-enterprise-platform#stacklok/stacklok-enterprise-platform#376
Description
Store the
serverNameparameter on the CedarAuthorizerstruct and update theNewCedarAuthorizerconstructor to accept it. TheAuthorizerFactory.CreateAuthorizerinterface already passesserverNameto the Cedar factory, but the Cedar implementation discards it with_ string. This task makes the authorizer retain the value so that downstream changes (server-scoped resource entities, MCP-based list operations, group extraction) can use it.This is a prerequisite for server-scoped authorization in the enterprise platform. Without
serverNamestored on the authorizer, compiled Cedar policies that useresource in MCP::"<server>"cannot be evaluated correctly.Context
The Platform Authorization design extends the existing
cedarv1authorizer with optional enterprise capabilities. Several downstream OSS changes (#4768, #4769, #4770) depend onserverNamebeing available on theAuthorizerstruct. Currently the factory discards it, so the struct has no way to reference the server identity at authorization time.The change is backward-compatible: when
serverNameis empty (standalone Cedar usage with no enterprise controller), the authorizer behaves identically to today.Dependencies: None (can start immediately)
Blocks: #4768 (group/role extraction), #4769 (MCP parent on resources), #4770 (FeatureType to MCP for list ops)
Acceptance Criteria
Authorizerstruct has aserverName stringfieldNewCedarAuthorizeracceptsserverName stringas a second parameterCreateAuthorizerfactory passesserverNamethrough toNewCedarAuthorizerinstead of discarding with_NewCedarAuthorizeracross 4 test files are updated to pass""as theserverNameargumentCreateAuthorizerfactory call on line 98 ofcore.gopassesserverNametoNewCedarAuthorizerserverNamepreserves identical behavior)serverNameis stored and accessible on the authorizerTechnical Approach
Recommended Implementation
Three changes in
pkg/authz/authorizers/cedar/core.go:serverNamefield toAuthorizerstruct (line 116):NewCedarAuthorizersignature (line 137):Store
serverNamein theAuthorizerstruct during construction:CreateAuthorizerfactory (line 88):""as the second argument to all 19NewCedarAuthorizer(...)call sites in test files. The empty string preserves identical behavior for every existing test.Patterns & Frameworks
core.go(functional struct initialization)testifyassertions per project conventionst.Parallel()on tests with no shared mutable stateCode Pointers
pkg/authz/authorizers/cedar/core.golines 86-99 --CreateAuthorizerfactory method; currently discardsserverNamewith_ string; needs to accept and pass throughpkg/authz/authorizers/cedar/core.golines 115-125 --Authorizerstruct definition; needs newserverNamefieldpkg/authz/authorizers/cedar/core.golines 136-167 --NewCedarAuthorizerconstructor; needsserverNameparameter and storagepkg/authz/authorizers/registry.golines 16-24 --AuthorizerFactoryinterface definition; already passesserverNametoCreateAuthorizerpkg/authz/authorizers/cedar/core_test.go-- 10 call sites to update (lines 74, 316, 341, 652, 713, 764, 804, 828, 917 useNewCedarAuthorizer(ConfigOptions{...}))pkg/authz/middleware_test.go-- 4 call sites (lines 33, 417, 783, 953)pkg/authz/response_filter_test.go-- 3 call sites (lines 26, 221, 270)pkg/authz/integration_test.go-- 2 call sites (lines 30, 332)Component Interfaces
The function signature change:
The factory signature is unchanged (it already accepts
serverName):Testing Strategy
Unit Tests
NewCedarAuthorizerwith non-emptyserverNamestores the value on the struct (cast to*Authorizerand verifyserverNamefield)NewCedarAuthorizerwith emptyserverNamestores empty string (backward compat)CreateAuthorizerfactory passesserverNamethrough (create via factory with"my-server", cast result, verify field)Existing Test Validation
core_test.gocompile and pass with""addedmiddleware_test.gocompile and pass with""addedresponse_filter_test.gocompile and pass with""addedintegration_test.gocompile and pass with""addedEdge Cases
NewCedarAuthorizerwithserverNamecontaining special characters (e.g.,"my-server.example.com") stores correctly -- no sanitization expected at this layerOut of Scope
serverNamein authorization logic (Set MCP parent on resource entities #4769 adds MCP parent; Replace FeatureType with MCP for list operations #4770 uses it for list ops)GroupClaimName/RoleClaimNametoConfigOptions(Add RoleClaimName to ConfigOptions (complement existing GroupClaimName) #4763)References
pkg/authz/authorizers/registry.go--AuthorizerFactoryinterface already definesserverNameparameter