spocputil

package
v0.7.23 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: BSD-2-Clause Imports: 10 Imported by: 0

Documentation

Overview

Package spocputil provides shared utilities for working with SPOCP S-expressions, including parsing human-readable "advanced form" into sexp.Element trees and loading rules from files.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildTaggedQuery

func BuildTaggedQuery(tag string, dims []string, values map[string]string) sexp.Element

BuildTaggedQuery constructs `(tag (dim1 val1)(dim2 val2)...)` in the given dimension order. A dimension with no matching entry in `values` is emitted as an empty list `(dim)`, which SPOCP wildcard rules match.

func LoadRulesFromFile

func LoadRulesFromFile(engine *spocp.AdaptiveEngine, path string, validate func(elem sexp.Element, source string) error) error

LoadRulesFromFile reads human-readable SPOCP rules (one per line) from a file and adds them to the engine using ParseAdvancedSExp. If validate is non-nil, each parsed rule is passed to it (with a source string identifying the file and line number) before being added; a returned error aborts loading.

func ParseAdvancedSExp

func ParseAdvancedSExp(input string) (sexp.Element, error)

ParseAdvancedSExp parses a human-readable ("advanced form") S-expression into a sexp.Element. This allows users to write rules as:

(credential (scope org_cred)(acr urn:example:loa3)(email_verified true))

instead of canonical form:

(10:credential(5:scope8:org_cred)(3:acr19:urn:example:loa3)(14:email_verified4:true))

It also supports star forms:

(*)                        → wildcard
(* prefix urn:example:)    → prefix match
(* suffix @example.com)    → suffix match
(* set loa3 loa4)          → set match

func ValidateRuleElement

func ValidateRuleElement(elem sexp.Element, tag string, dims []string, requireValue bool, source string) error

ValidateRuleElement checks that a parsed rule is a list tagged `tag` containing exactly len(dims) sub-lists in the given order, each tagged with the corresponding dimension name. SPOCP matching is positional, so wrong order, wrong count, or an unexpectedly multi-valued part would silently never match at query time -- reject those up front instead.

requireValue controls whether a dimension may be written as an empty list, e.g. (email_verified), to mean "any value" (SPOCP's native wildcard form, and what BuildTaggedQuery itself emits for an absent value):

  • true: every dimension must hold exactly one value; write the literal atom "*" for wildcards. This is the endpoint-access convention.
  • false: a dimension may hold zero or one value; more than one is always rejected regardless. This is the issuance-policy convention, which already authors wildcards as empty dimensions.

The two domains' existing rule files use different conventions here, so this can't be collapsed into one fixed choice without either breaking issuance's existing wildcard style or silently loosening endpoint-access validation.

Types

type Engine

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

Engine wraps a SPOCP AdaptiveEngine with a mutex, safe for concurrent QueryElement/RuleCount/ExportRules access after construction. Shared by both HTTP endpoint-access rules (pkg/httphelpers) and OIDC credential-issuance policy rules (pkg/issuance), so both rule sets are built, validated, and queried through the same machinery rather than each maintaining its own copy.

func BuildEngine

func BuildEngine(tag string, dims []string, requireValue bool, ruleLabel string, inlineRules []string, rulesFile string) (*Engine, error)

BuildEngine parses inline rules and an optional rules file into a new Engine tagged for this rule set's domain. Returns (nil, nil) when neither inline rules nor a rules file are configured.

ruleLabel names the rule set for error messages (e.g. "SPOCP" or "issuance policy") so callers get a domain-specific error without each needing to re-wrap it.

When dims is non-nil, every rule -- inline and file-loaded alike -- is validated against tag/dims via ValidateRuleElement before being added. Pass nil to skip shape validation for rule sets whose dimensions aren't statically fixed (e.g. an issuance policy with no configured QueryTemplate, which queries with whatever OIDC claims happen to be present on a given request rather than a fixed, known set of dimensions).

requireValue is forwarded to ValidateRuleElement -- see its doc comment for why this can't be a fixed choice shared by every caller.

func (*Engine) ExportRules

func (e *Engine) ExportRules() []sexp.Element

ExportRules returns the raw parsed rule elements (read-locked), for callers that need to inspect rule structure directly (e.g. resolving the set of resources a subject is authorized for, rather than just answering yes/no for one specific query).

func (*Engine) QueryElement

func (e *Engine) QueryElement(q sexp.Element) bool

QueryElement checks if the query is authorized (read-locked).

func (*Engine) RuleCount

func (e *Engine) RuleCount() int

RuleCount returns the number of loaded rules (read-locked).

Jump to

Keyboard shortcuts

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