Skip to content

feat: support custom request dimensions in step.authz_check_casbin via extra_fields#15

Merged
intel352 merged 3 commits intomainfrom
copilot/add-custom-request-dimensions
Mar 12, 2026
Merged

feat: support custom request dimensions in step.authz_check_casbin via extra_fields#15
intel352 merged 3 commits intomainfrom
copilot/add-custom-request-dimensions

Conversation

Copy link
Contributor

Copilot AI commented Mar 12, 2026

step.authz_check_casbin was limited to the 3-tuple Casbin request (sub, obj, act), making per-tenant authorization impossible without hacks like encoding tenant in the object path.

Changes

  • extra_fields config on step.authz_check_casbin — ordered list of {key, value} pairs inserted between sub and (obj, act), producing tuples like (sub, tenant, obj, act). Each value supports Go template syntax. Validated at config-parse time: returns an error if extra_fields is not a list, if any entry is missing key or value, or if duplicate keys are present.
  • Nested {{.steps.<name>.<key>}} template accessbuildTemplateData now exposes step outputs under a steps map (only when triggerData does not already define a steps key) in addition to the existing flat merge, enabling templates to reference specific prior step outputs. Note: dot-notation only works for valid Go identifiers; use {{ index .steps "step-name" "key" }} for step names containing dashes or other special characters.
  • CasbinModule.Enforce accepts extra ...string — variadic extra dims forwarded to Casbin as [sub, extra..., obj, act]; fully backward compatible with existing 3-tuple models.
  • Extra fields included in audit_event — resolved extra field values appear nested under audit_event.extra_fields (a sub-map keyed by field name) to avoid collisions with reserved audit keys such as type, subject, action, etc.

Example

- type: step.authz_check_casbin
  config:
    module: authz
    subject_key: auth_user_id
    object: /v1/follow-ups
    action: "{{.request_method}}"
    extra_fields:
      - key: tenant
        value: "{{.steps.auth.affiliate_id}}"

Matches a 4-tuple Casbin model:

[request_definition]
r = sub, tenant, obj, act

[policy_definition]
p = sub, tenant, obj, act
Original prompt

This section details on the original issue you should resolve

<issue_title>feat: support custom request dimensions in step.authz_check (e.g., tenant)</issue_title>
<issue_description>## Problem

The step.authz_check step currently only supports the 3-tuple Casbin request (sub, obj, act). Multi-tenant applications need to include a tenant dimension in authorization decisions, requiring a 4+ tuple request like (sub, tenant, obj, act).

Proposed Solution

Add an extra_fields config to step.authz_check that allows specifying additional request parameters. Each field can be a static value or a Go template:

- type: step.authz_check
  config:
    module: authz
    subject_key: auth_user_id
    object: /v1/follow-ups
    action: "{{.request_method}}"
    extra_fields:
      - key: tenant
        value: "{{.steps.auth.affiliate_id}}"

The Enforce call would then pass (sub, tenant, obj, act) to match a 4-tuple policy definition.

Use Case

Crisis Text Line's Chimera platform uses database.partitioned for per-tenant data isolation and needs per-tenant authorization policies. Different affiliates may have different role definitions and resource permissions.

Alternatives Considered

  • Encoding tenant in the object path (tenant:/path) — works but is a hack
  • Multiple module instances per tenant — does not scale dynamically</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

…_fields

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for custom request dimensions in step.authz_check feat: support custom request dimensions in step.authz_check via extra_fields Mar 12, 2026
@intel352 intel352 marked this pull request as ready for review March 12, 2026 13:58
Copilot AI review requested due to automatic review settings March 12, 2026 13:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for multi-dimensional Casbin authorization requests in step.authz_check_casbin by allowing configurable extra_fields and improving template data to reference prior step outputs without key collisions.

Changes:

  • Add extra_fields to step.authz_check_casbin and include resolved values in audit_event.
  • Extend template data with a nested .steps map for referencing specific prior step outputs.
  • Update CasbinModule.Enforce (and wrapper) to accept variadic extra request dimensions and forward them as (sub, extra..., obj, act).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
internal/step_authz_check.go Parses/resolves extra_fields, forwards extra dimensions into enforcement, expands template data with nested .steps, and augments audit output.
internal/module_casbin.go Extends Enforce to support extra request dimensions and forwards them to Casbin in the desired order.
authz/authz.go Updates the public wrapper’s Enforce signature to match the new variadic API.
internal/step_authz_check_test.go Adds tests covering static/template/nested-step extra fields and audit output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +145 to +149
return nil, fmt.Errorf("step.authz_check_casbin %q: extra_fields[%d] must be a map with \"key\" and \"value\"", name, i)
}
key, _ := item["key"].(string)
val, _ := item["value"].(string)
if key == "" {
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description/examples refer to step.authz_check, but this repository’s step type appears to be step.authz_check_casbin (as reflected in the step’s error messages here). Consider aligning the PR description/README/examples so users don’t configure a non-existent step type.

Copilot uses AI. Check for mistakes.
Comment on lines 237 to +241
}
for i, ef := range s.extraFields {
evt[ef.key] = extraVals[i]
}
result.Output["audit_event"] = evt
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra_fields keys are written directly into audit_event, so a user-supplied key like type, subject, allowed, etc. would overwrite core audit fields (and could change types). Consider rejecting reserved keys at config-parse time or nesting extras under a dedicated key (e.g., audit_event.extra_fields).

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +159
ef := extraField{key: key, value: val}
if isTemplate(val) {
ef.tmpl, err = template.New(fmt.Sprintf("extra_field_%d", i)).Parse(val)
if err != nil {
return nil, fmt.Errorf("step.authz_check_casbin %q: parse extra_fields[%d] template: %w", name, i, err)
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s no validation for duplicate extra_fields keys. If the config repeats a key, the later value will overwrite the earlier one in audit_event, producing confusing/incomplete audit output. Consider validating keys are unique (or structuring audit output to preserve duplicates).

Copilot uses AI. Check for mistakes.
@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread
Also fix the linter errors

…nt structure

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 12, 2026

@copilot apply changes based on the comments in this thread
Also fix the linter errors

Applied all review feedback in f232ba1:

  • extra_fields type validation: Now returns a clear error when extra_fields is present but not a list (was silently ignored before)
  • Duplicate key validation: Returns an error at config-parse time for duplicate extra_fields keys
  • Audit event collision: Extra fields are now nested under audit_event.extra_fields (a map[string]any) instead of being written directly to the audit event top level — no risk of overwriting reserved keys like type, subject, etc.
  • steps key collision: buildTemplateData only injects the steps map when triggerData doesn't already define one
  • Template docs: buildTemplateData now documents that dot-notation only works for valid Go identifiers and recommends {{ index .steps "step-name" "key" }} for step names with dashes
  • Unsafe type assertion in test: Fixed to use the safe audit, ok := ... pattern with t.Fatalf
  • gofmt formatting: Fixed struct field alignment in authzCheckStep

Two new tests added: TestAuthzCheckStep_ExtraFieldsNotAList and TestAuthzCheckStep_ExtraFieldsDuplicateKey.

Copilot AI changed the title feat: support custom request dimensions in step.authz_check via extra_fields feat: support custom request dimensions in step.authz_check_casbin via extra_fields Mar 12, 2026
@intel352 intel352 merged commit 026af27 into main Mar 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support custom request dimensions in step.authz_check (e.g., tenant)

3 participants