persona

package
v1.67.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 24, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package persona provides persona-based access control and customization.

Index

Constants

View Source
const (
	ActionAllow = "allow"
	ActionDeny  = "deny"
)

API route action values. Empty Action defaults to ActionAllow.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIRouteRule added in v1.59.0

type APIRouteRule struct {
	// Connection is a glob (e.g. "crm-*") matched against the connection
	// name. Required.
	Connection string `json:"connection" yaml:"connection"`

	// Methods is a list of HTTP method globs (e.g. ["GET", "HEAD"]).
	// Empty = any method. Patterns are case-sensitive — typically
	// uppercase ("GET", "POST", etc.) since the toolkit normalizes
	// inbound methods to uppercase before this check runs.
	Methods []string `json:"methods,omitempty" yaml:"methods,omitempty"`

	// Paths is a list of path globs (e.g. ["/v1/users/*", "/v1/orders/**"]).
	// Empty = any path.
	Paths []string `json:"paths,omitempty" yaml:"paths,omitempty"`

	// Action is "allow" (default) or "deny". Deny rules take precedence
	// over allow rules within the matching-Connection subset.
	Action string `json:"action,omitempty" yaml:"action,omitempty"`
}

APIRouteRule constrains the HTTP API gateway's api_invoke_endpoint tool to specific (method, path) combinations on connections matched by Connection. A persona's APIRoutes list is consulted only for kind=api connections; other toolkit kinds ignore it.

Semantics, evaluated against a single (connection, method, path) tuple:

  • Rules whose Connection glob does not match are skipped.
  • Among matching rules: any "deny" rule whose Methods+Paths match denies the call (deny takes precedence).
  • Otherwise, at least one "allow" rule whose Methods+Paths match must be present for the call to be authorized.
  • If NO rule matches the connection at all, the API route check is a no-op — the existing ConnectionRules check is the sole gate (backward-compatible behavior).

Empty Methods or Paths slices mean "any" — a rule with empty Methods and Paths matches every method+path combination on the connection.

type AccessDecision added in v1.57.0

type AccessDecision struct {
	Allowed        bool         `json:"allowed"`
	MatchedPattern string       `json:"matched_pattern"`
	Source         AccessSource `json:"source"`
}

AccessDecision is the per-tool decision a persona produces for a specific tool name, with the matching pattern and source recorded so the admin UI can render "why".

type AccessSource added in v1.57.0

type AccessSource string

AccessSource indicates which clause of a persona's tool rules produced an allow/deny decision. Used by the admin Tools-detail endpoint so operators can see WHY a persona allows or denies a tool.

const (
	// AccessSourceAllow — an explicit allow pattern matched.
	AccessSourceAllow AccessSource = "allow"
	// AccessSourceDeny — an explicit deny pattern matched (takes precedence over allow).
	AccessSourceDeny AccessSource = "deny"
	// AccessSourceDefault — no allow pattern matched; falls through to fail-closed default.
	AccessSourceDefault AccessSource = "default"
)

type Authorizer added in v0.14.0

type Authorizer struct {
	// contains filtered or unexported fields
}

Authorizer implements middleware.Authorizer using personas.

func NewAuthorizer added in v0.14.0

func NewAuthorizer(registry *Registry, mapper RoleMapper) *Authorizer

NewAuthorizer creates a new persona-based authorizer.

func (*Authorizer) IsAPIRouteAllowed added in v1.59.0

func (a *Authorizer) IsAPIRouteAllowed(ctx context.Context, roles []string, connection, method, path string) (allowed bool, personaName, reason string)

IsAPIRouteAllowed authorizes (method, path) on the named HTTP API gateway connection for the user with the given roles. Layered on top of IsAuthorized: the caller (the api gateway toolkit) is expected to have already passed the tool/connection-level gate via the standard MCP middleware, and this method adds per-route granularity. Returns the resolved persona name (for audit) and a reason on denial.

func (*Authorizer) IsAuthorized added in v0.14.0

func (a *Authorizer) IsAuthorized(ctx context.Context, _ string, roles []string, toolName, connectionName string) (allowed bool, personaName, reason string)

IsAuthorized checks if the user is authorized for the tool on the given connection. Both the tool and the connection must be allowed by the persona's rules. Returns the resolved persona name for audit logging.

type ChainedRoleMapper

type ChainedRoleMapper struct {
	Mappers []RoleMapper
}

ChainedRoleMapper tries multiple mappers in order.

func (*ChainedRoleMapper) MapToPersona

func (c *ChainedRoleMapper) MapToPersona(ctx context.Context, roles []string) (*Persona, error)

MapToPersona uses the first mapper that returns a persona.

func (*ChainedRoleMapper) MapToRoles

func (c *ChainedRoleMapper) MapToRoles(claims map[string]any) ([]string, error)

MapToRoles aggregates roles from all mappers.

type Config added in v0.14.0

type Config struct {
	DisplayName string                `yaml:"display_name"`
	Description string                `yaml:"description,omitempty"`
	Roles       []string              `yaml:"roles"`
	Tools       ToolRulesConfig       `yaml:"tools"`
	Connections ConnectionRulesConfig `yaml:"connections"`
	Context     ContextOverridesYAML  `yaml:"context"`
	Priority    int                   `yaml:"priority,omitempty"`
}

Config is the configuration format for personas.

type ConnectionRules added in v1.48.0

type ConnectionRules struct {
	// Allow patterns for allowed connections (supports wildcards like "prod-*").
	Allow []string `json:"allow,omitempty" yaml:"allow,omitempty"`

	// Deny patterns for denied connections (takes precedence over Allow).
	Deny []string `json:"deny,omitempty" yaml:"deny,omitempty"`
}

ConnectionRules defines connection-level access rules for a persona. These work alongside ToolRules — a tool call must pass both the tool check AND the connection check. If the Allow list is empty, all connections are permitted (backward-compatible default).

type ConnectionRulesConfig added in v1.48.0

type ConnectionRulesConfig struct {
	Allow []string `yaml:"allow,omitempty"`
	Deny  []string `yaml:"deny,omitempty"`
}

ConnectionRulesConfig is the YAML configuration for connection rules.

type ContextOverrides added in v1.48.0

type ContextOverrides struct {
	// DescriptionPrefix is prepended to the server description (separated by
	// a blank line). Use this to add persona-specific context before the
	// base platform description. Ignored if DescriptionOverride is set.
	DescriptionPrefix string `json:"description_prefix,omitempty" yaml:"description_prefix,omitempty"`

	// DescriptionOverride replaces the server description entirely.
	// Use this when a persona needs a completely different description.
	DescriptionOverride string `json:"description_override,omitempty" yaml:"description_override,omitempty"`

	// AgentInstructionsSuffix is appended to the server agent instructions
	// (separated by a blank line). Use this to add persona-specific guidance
	// after the base instructions. Ignored if AgentInstructionsOverride is set.
	AgentInstructionsSuffix string `json:"agent_instructions_suffix,omitempty" yaml:"agent_instructions_suffix,omitempty"`

	// AgentInstructionsOverride replaces the server agent instructions entirely.
	// Use this when a persona needs completely different instructions.
	AgentInstructionsOverride string `json:"agent_instructions_override,omitempty" yaml:"agent_instructions_override,omitempty"`
}

ContextOverrides defines per-persona overrides for the description and agent instructions that the platform_info tool returns. These let you tailor what an AI agent sees based on who is using the platform.

For each field pair (prefix/override), the override takes precedence. If an override is set, the prefix/suffix is ignored.

type ContextOverridesYAML added in v1.48.0

type ContextOverridesYAML struct {
	DescriptionPrefix         string `yaml:"description_prefix,omitempty"`
	DescriptionOverride       string `yaml:"description_override,omitempty"`
	AgentInstructionsSuffix   string `yaml:"agent_instructions_suffix,omitempty"`
	AgentInstructionsOverride string `yaml:"agent_instructions_override,omitempty"`
}

ContextOverridesYAML is the YAML configuration for context overrides.

type OIDCRoleMapper

type OIDCRoleMapper struct {
	// ClaimPath is the dot-separated path to roles in claims.
	ClaimPath string

	// RolePrefix filters roles to those starting with this prefix.
	RolePrefix string

	// PersonaMapping maps roles to persona names.
	PersonaMapping map[string]string

	// Registry is the persona registry.
	Registry *Registry
}

OIDCRoleMapper extracts roles from OIDC token claims.

func (*OIDCRoleMapper) MapToPersona

func (m *OIDCRoleMapper) MapToPersona(_ context.Context, roles []string) (*Persona, error)

MapToPersona maps roles to a persona.

func (*OIDCRoleMapper) MapToRoles

func (m *OIDCRoleMapper) MapToRoles(claims map[string]any) ([]string, error)

MapToRoles extracts roles from OIDC claims.

type Persona

type Persona struct {
	// Name is the unique identifier for this persona.
	Name string `json:"name" yaml:"name"`

	// DisplayName is the human-readable name.
	DisplayName string `json:"display_name" yaml:"display_name"`

	// Description describes this persona.
	Description string `json:"description,omitempty" yaml:"description,omitempty"`

	// Roles are the roles that map to this persona. When a user authenticates
	// via OIDC, their token claims are mapped to roles. When using API keys,
	// roles are assigned directly. A user gets this persona when any of their
	// roles match.
	Roles []string `json:"roles" yaml:"roles"`

	// Tools defines tool access rules (allow/deny glob patterns).
	Tools ToolRules `json:"tools" yaml:"tools"`

	// Connections defines connection-level access rules. A tool call must pass
	// both the tool check and the connection check. If Connections.Allow is
	// empty, all connections are permitted (backward-compatible default).
	Connections ConnectionRules `json:"connections" yaml:"connections"`

	// APIRoutes defines per-(connection, method, path) rules for the HTTP
	// API gateway toolkit (kind=api). Layered on top of Connections: if a
	// persona is allowed a connection at the tool/connection level, APIRoutes
	// can further restrict which HTTP methods and paths the model can invoke
	// against that connection. Personas with no APIRoutes entries for a
	// given connection get the existing connection-level behavior unchanged
	// (backward-compatible). See pkg/persona/filter.go IsAPIRouteAllowed.
	APIRoutes []APIRouteRule `json:"api_routes,omitempty" yaml:"api_routes,omitempty"`

	// Context defines per-persona overrides for the platform description and
	// agent instructions returned by the platform_info tool.
	Context ContextOverrides `json:"context" yaml:"context"`

	// Priority determines which persona takes precedence when a user's roles
	// match multiple personas. Higher values win. Default is 0; the built-in
	// admin persona uses 100.
	Priority int `json:"priority,omitempty" yaml:"priority,omitempty"`

	// Source indicates where this persona was loaded from at runtime.
	// Values: "file" (YAML config), "database" (DB-managed), "both" (file
	// with DB override). This is runtime metadata — not persisted.
	Source string `json:"source,omitempty" yaml:"-"`
}

Persona defines a user persona with associated permissions and customizations.

func AdminPersona

func AdminPersona() *Persona

AdminPersona creates an admin persona with full access.

func DefaultPersona

func DefaultPersona() *Persona

DefaultPersona creates a default persona that denies all access. This ensures fail-closed behavior - users must be explicitly granted access.

func (*Persona) ApplyAgentInstructions added in v1.48.0

func (p *Persona) ApplyAgentInstructions(base string) string

ApplyAgentInstructions returns the effective agent instructions for this persona. If AgentInstructionsOverride is set, it replaces the base entirely. If AgentInstructionsSuffix is set, it is appended to the base. Otherwise the base is returned unchanged.

func (*Persona) ApplyDescription added in v1.48.0

func (p *Persona) ApplyDescription(base string) string

ApplyDescription returns the effective description for this persona. If DescriptionOverride is set, it replaces the base entirely. If DescriptionPrefix is set, it is prepended to the base. Otherwise the base is returned unchanged.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry manages persona definitions.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new persona registry.

func (*Registry) All

func (r *Registry) All() []*Persona

All returns all registered personas.

func (*Registry) DefaultName added in v0.17.0

func (r *Registry) DefaultName() string

DefaultName returns the default persona name.

func (*Registry) Get

func (r *Registry) Get(name string) (*Persona, bool)

Get retrieves a persona by name.

func (*Registry) GetDefault

func (r *Registry) GetDefault() (*Persona, bool)

GetDefault returns the default persona.

func (*Registry) GetForRoles

func (r *Registry) GetForRoles(roles []string) (*Persona, bool)

GetForRoles returns the best matching persona for the given roles.

func (*Registry) LoadFromConfig

func (r *Registry) LoadFromConfig(config map[string]*Config) error

LoadFromConfig loads personas from a configuration map.

func (*Registry) Register

func (r *Registry) Register(p *Persona) error

Register adds a persona to the registry.

func (*Registry) SetDefault

func (r *Registry) SetDefault(name string)

SetDefault sets the default persona name.

func (*Registry) Unregister added in v0.17.0

func (r *Registry) Unregister(name string) error

Unregister removes a persona by name. Returns error if not found.

type RoleMapper

type RoleMapper interface {
	// MapToRoles extracts roles from claims.
	MapToRoles(claims map[string]any) ([]string, error)

	// MapToPersona maps roles to a persona.
	MapToPersona(ctx context.Context, roles []string) (*Persona, error)
}

RoleMapper maps identity claims to platform roles and personas.

type StaticRoleMapper

type StaticRoleMapper struct {
	// GroupPersonas maps groups to persona names.
	GroupPersonas map[string]string

	// DefaultPersonaName is the fallback persona.
	DefaultPersonaName string

	// Registry is the persona registry.
	Registry *Registry
}

StaticRoleMapper uses static configuration for mapping.

func (*StaticRoleMapper) MapToPersona

func (m *StaticRoleMapper) MapToPersona(_ context.Context, _ []string) (*Persona, error)

MapToPersona maps based on static configuration.

func (*StaticRoleMapper) MapToRoles

func (*StaticRoleMapper) MapToRoles(_ map[string]any) ([]string, error)

MapToRoles returns static roles (not used for static mapping).

type ToolFilter

type ToolFilter struct {
	// contains filtered or unexported fields
}

ToolFilter filters tools based on persona rules.

func NewToolFilter

func NewToolFilter(registry *Registry) *ToolFilter

NewToolFilter creates a new tool filter.

func (*ToolFilter) FilterTools

func (f *ToolFilter) FilterTools(persona *Persona, tools []string) []string

FilterTools filters a list of tools based on persona rules.

func (*ToolFilter) IsAPIRouteAllowed added in v1.59.0

func (*ToolFilter) IsAPIRouteAllowed(persona *Persona, connection, method, path string) bool

IsAPIRouteAllowed reports whether the persona may invoke (method, path) on the named connection through the HTTP API gateway. Layered on top of IsConnectionAllowed: callers MUST also pass the connection-level gate; this function adds per-(method, path) granularity for kind=api connections.

Semantics (also documented on APIRouteRule):

  • If no APIRoutes entry's Connection glob matches the connection, the check is a no-op (returns true) — the existing connection-level check is the sole gate, preserving backward-compat for personas written before APIRoutes existed.
  • If at least one APIRoutes entry matches the connection, the call must pass: no matching deny rule, AND at least one matching allow rule.
  • A nil persona always denies (fail-closed).

func (*ToolFilter) IsAllowed

func (*ToolFilter) IsAllowed(persona *Persona, toolName string) bool

IsAllowed checks if a tool is allowed for a persona.

func (*ToolFilter) IsConnectionAllowed added in v1.48.0

func (*ToolFilter) IsConnectionAllowed(persona *Persona, connectionName string) bool

IsConnectionAllowed checks if a connection is allowed for a persona. If the persona has no connection allow rules, all connections are permitted (backward-compatible default). Empty connection names are always allowed.

func (*ToolFilter) WhyAllowed added in v1.57.0

func (*ToolFilter) WhyAllowed(persona *Persona, toolName string) AccessDecision

WhyAllowed returns the access decision for a tool name with the matched pattern and source recorded. Mirrors IsAllowed's logic (deny first, allow second, default deny last) but surfaces which clause produced the decision so callers can explain it to operators.

type ToolRules

type ToolRules struct {
	// Allow patterns for allowed tools (supports wildcards like "trino_*").
	Allow []string `json:"allow" yaml:"allow"`

	// Deny patterns for denied tools (takes precedence over Allow).
	Deny []string `json:"deny" yaml:"deny"`
}

ToolRules defines tool access rules for a persona.

type ToolRulesConfig

type ToolRulesConfig struct {
	Allow []string `yaml:"allow"`
	Deny  []string `yaml:"deny"`
}

ToolRulesConfig is the YAML configuration for tool rules.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL