Documentation
¶
Overview ¶
Package primitives holds small, general-purpose claim-derivation helpers that credentials can wire in via configuration (see e.g. pkg/model/data_sources.go's PresentationScope.Derivations). Each primitive is a pure function with a matching Args struct + Apply method used by the Derivation dispatcher.
Index ¶
- Constants
- func AgeOverThresholds(birthdate string, now time.Time, thresholds []int) (map[string]bool, error)
- func CountryAlpha2(s string) string
- func CountryAlpha3(s string) string
- func FieldNames() []string
- func LowerCase(s string) string
- func SWAMIDHighestAssuranceLevel(value any) string
- func Trim(s string) string
- func UpperCase(s string) string
- func YYYYMMDDToISO(s string) (string, error)
- type AgeOverThresholdsArgs
- type Applier
- type CountryAlpha2Args
- type CountryAlpha3Args
- type Derivation
- type LowercaseArgs
- type RandomArgs
- type SWAMIDHighestAssuranceLevelArgs
- type TrimArgs
- type UppercaseArgs
- type YYYYMMDDToISOArgs
Constants ¶
const ( // RandomFormatUUID is a version 4 UUID in canonical textual form. RandomFormatUUID = "uuid" // RandomFormatHex is lower-case hex over RandomArgs.Bytes random bytes. RandomFormatHex = "hex" // RandomFormatBase64URL is unpadded base64url over RandomArgs.Bytes // random bytes. RandomFormatBase64URL = "base64url" )
Random value formats. The names are the configured `format` values.
Variables ¶
This section is empty.
Functions ¶
func AgeOverThresholds ¶
AgeOverThresholds parses birthdate (ISO YYYY-MM-DD) and returns a map containing, for every threshold N: age_over_N (true if the subject had completed N years at `now`) and over_N_this_year (true if the subject reaches N at some point in `now`'s calendar year, i.e. `now.Year() - birthYear >= N`). A zero `now` falls back to time.Now(). Returns an error if birthdate is empty, malformed, or in the future. Callers pick which thresholds are meaningful for their credential type.
func CountryAlpha2 ¶
CountryAlpha2 maps a country name or alpha-3 code to its ISO 3166-1 alpha-2 code. Unknown inputs are returned unchanged so an unrecognised value does not silently become empty.
func CountryAlpha3 ¶
CountryAlpha3 maps a country name or alpha-2 code to its ISO 3166-1 alpha-3 code. Unknown inputs are returned unchanged.
func FieldNames ¶
func FieldNames() []string
FieldNames returns the YAML names of every primitive field on Derivation in declaration order. Used by gen_config_docs to enumerate primitives.
func SWAMIDHighestAssuranceLevel ¶
SWAMIDHighestAssuranceLevel reads a SAML eduPersonAssurance claim value (single string, []string, or []any of strings) and returns the strongest recognised SWAMID Assurance Framework URI. Non-SWAMID URIs (including REFEDS IAP) are ignored. Returns "" when the value is missing, of an unsupported type, or holds no SWAMID URI.
func YYYYMMDDToISO ¶
YYYYMMDDToISO converts SCHAC schacDateOfBirth ("YYYYMMDD") to ISO full-date ("YYYY-MM-DD"). time.Parse validates day-of-month, so impossible dates like "20240230" surface as an error instead of being emitted verbatim.
Types ¶
type AgeOverThresholdsArgs ¶
type AgeOverThresholdsArgs struct {
// Input is the birthdate claim name (value must be ISO YYYY-MM-DD).
Input string `yaml:"input" validate:"required" doc_example:"birthdate"`
// Thresholds are the ages (in years) to expose. Each N produces
// age_over_N and over_N_this_year (booleans). Every entry must be
// positive.
Thresholds []int `yaml:"thresholds" validate:"required,min=1,dive,gt=0" doc_example:"[13, 15, 18, 21, 65]"`
}
AgeOverThresholdsArgs configures the age_over_thresholds primitive. Emits two boolean claims per threshold: age_over_N (completed years at `now`) and over_N_this_year (reaches N at some point in `now`'s calendar year, i.e. year(now) - year(birthdate) >= N).
type Applier ¶
Applier is the shape every primitive Args struct implements. Every pointer field on Derivation must point at a type implementing Applier; the reflection-based dispatcher below relies on it.
type CountryAlpha2Args ¶
type CountryAlpha2Args struct {
Input string `yaml:"input" validate:"required" doc_example:"nationalities"`
Output string `yaml:"output,omitempty" doc_example:"nationalities"`
}
CountryAlpha2Args configures the country_alpha2 primitive. Unknown inputs pass through unchanged; applied element-wise on []string.
type CountryAlpha3Args ¶
type CountryAlpha3Args struct {
Input string `yaml:"input" validate:"required" doc_example:"nationalities"`
Output string `yaml:"output,omitempty" doc_example:"nationalities"`
}
CountryAlpha3Args configures the country_alpha3 primitive.
type Derivation ¶
type Derivation struct {
// AgeOverThresholds emits two boolean claims per configured threshold
// N from an ISO YYYY-MM-DD birthdate: age_over_N (completed years at
// `now`) and over_N_this_year (reaches N at some point in `now`'s
// calendar year).
AgeOverThresholds *AgeOverThresholdsArgs `yaml:"age_over_thresholds,omitempty"`
// Lowercase applies strings.ToLower elementwise.
Lowercase *LowercaseArgs `yaml:"lowercase,omitempty"`
// Uppercase applies strings.ToUpper elementwise.
Uppercase *UppercaseArgs `yaml:"uppercase,omitempty"`
// Trim applies strings.TrimSpace elementwise.
Trim *TrimArgs `yaml:"trim,omitempty"`
// CountryAlpha2 maps country names or alpha-3 codes to ISO 3166-1
// alpha-2 codes elementwise. Unknown inputs pass through unchanged.
CountryAlpha2 *CountryAlpha2Args `yaml:"country_alpha2,omitempty"`
// CountryAlpha3 maps country names or alpha-2 codes to ISO 3166-1
// alpha-3 codes elementwise. Unknown inputs pass through unchanged.
CountryAlpha3 *CountryAlpha3Args `yaml:"country_alpha3,omitempty"`
// YYYYMMDDToISO converts a SCHAC schacDateOfBirth ("YYYYMMDD") claim to
// ISO full-date ("YYYY-MM-DD"). Impossible calendar dates are an error.
YYYYMMDDToISO *YYYYMMDDToISOArgs `yaml:"yyyymmdd_to_iso,omitempty"`
// SWAMIDHighestAssuranceLevel reduces a multi-valued eduPersonAssurance
// claim to the strongest recognised SWAMID Assurance Framework URI.
SWAMIDHighestAssuranceLevel *SWAMIDHighestAssuranceLevelArgs `yaml:"swamid_highest_assurance_level,omitempty"`
// Random writes a freshly generated random value to a claim the source
// data did not supply - a document identifier, typically. The only
// primitive here that reads nothing and is not a pure function of its
// input; see RandomArgs for where the value is generated and how long
// it lives.
Random *RandomArgs `yaml:"random,omitempty"`
}
Derivation is one post-verification claim computation configured on a scope; exactly one field must be set and that field's Apply method runs.
Each list entry under a scope's `derivations` field is keyed by primitive name (e.g. `age_over_thresholds:` or `lowercase: { input: email }`). The subsections below catalog the primitives and their parameters.
func (*Derivation) Apply ¶
Apply runs whichever primitive is configured on d. Returns an error when zero or more than one field is set; the struct-level validator catches that case at config load time, but Apply stays defensive for callers that build a Derivation programmatically (tests, bootstrapping tools).
func (*Derivation) SelectedName ¶
func (d *Derivation) SelectedName() string
SelectedName returns the YAML key of the configured primitive, or "" when zero or many fields are set. Provided for error-context callers.
type LowercaseArgs ¶
type LowercaseArgs struct {
// Input is the source claim name. Supports dot-notation claim paths
// (e.g. "identity.email") so this primitive can target the same nested
// claims that AttributeMapper materialises.
Input string `yaml:"input" validate:"required" doc_example:"email"`
// Output is the target claim name. Defaults to Input (in-place).
// Supports dot-notation claim paths.
Output string `yaml:"output,omitempty" doc_example:"email"`
}
LowercaseArgs configures the lowercase primitive. Scalar strings and []string are handled; other types are an error.
type RandomArgs ¶ added in v0.7.23
type RandomArgs struct {
// Output is the target claim name. Supports dot-notation claim paths
// (e.g. "identity.document_id") so this primitive can target the same
// nested claims that AttributeMapper materialises.
//
// There is no Input: this primitive reads nothing.
Output string `yaml:"output" validate:"required" doc_example:"document_id"`
// Format selects the shape of the value: "uuid" (the default), "hex" or
// "base64url".
Format string `yaml:"format,omitempty" validate:"omitempty,oneof=uuid hex base64url" doc_example:"uuid"`
// Bytes is how many random bytes back a "hex" or "base64url" value,
// between 8 and 64. Ignored for "uuid", whose length the format fixes.
// Defaults to 16.
Bytes int `yaml:"bytes,omitempty" validate:"omitempty,min=8,max=64" doc_example:"16"`
// Overwrite replaces a value the source data already supplied.
//
// False by default, and deliberately: this primitive exists to fill a
// claim authentication could not provide, and silently replacing a real
// identifier with a random one is a worse failure than leaving the
// derivation with nothing to do. Set it only where the claim is meant to
// be random every time regardless of what arrived.
Overwrite bool `yaml:"overwrite,omitempty"`
}
RandomArgs configures the random primitive.
It writes a freshly generated random value to a claim the source data did not supply. The use case is a claim a credential requires but authentication cannot provide - a document identifier that has to be unique across issued credentials being the one this was written for (SUNET/vc#736).
Where the value is generated decides how long it lives, because a derivation runs where its data source runs it:
- assertion sources (SAML, OIDC) run derivations in the ACS or callback, BEFORE the document is cached, so one value is generated per authentication and every credential issued from that session carries it - including each credential of a batch.
- other sources run derivations per credential request, so each request mints a new value, and a re-issuance will not match the first.
For an identifier meant to name a credential dataset, the first is what is wanted, which is also the case the issue describes.
Presentation sources are refused at config load. They run derivations twice from the same verified claims, with nothing carried between them - once to build the consent preview and again to build the credential - so a non-deterministic primitive would show the holder one identifier and issue another. Every other primitive is a function of its input and so agrees across both runs. See configuration.checkRandomDerivations.
Nothing here checks that a value has not been issued before. Uniqueness rests on the generator: a version 4 UUID has 122 random bits, and hex and base64url take theirs from crypto/rand. That is the same basis every other identifier in this system uses, and it is a statistical claim rather than an enforced one.
type SWAMIDHighestAssuranceLevelArgs ¶
type SWAMIDHighestAssuranceLevelArgs struct {
// Input is the source claim (string or list of strings).
Input string `yaml:"input" validate:"required" doc_example:"assurance_level"`
// Output is the target claim. Defaults to Input (in-place).
Output string `yaml:"output,omitempty" doc_example:"assurance_level"`
}
SWAMIDHighestAssuranceLevelArgs configures the swamid_highest_assurance_level primitive. Reduces a multi-valued eduPersonAssurance claim to the strongest recognised SWAMID URI; hard errors if the input is present but no SWAMID URI is recognised.
type TrimArgs ¶
type TrimArgs struct {
Input string `yaml:"input" validate:"required" doc_example:"name"`
Output string `yaml:"output,omitempty" doc_example:"name"`
}
TrimArgs configures the trim primitive.
type UppercaseArgs ¶
type UppercaseArgs struct {
Input string `yaml:"input" validate:"required" doc_example:"country"`
Output string `yaml:"output,omitempty" doc_example:"country"`
}
UppercaseArgs configures the uppercase primitive.
type YYYYMMDDToISOArgs ¶
type YYYYMMDDToISOArgs struct {
// Input is the source claim name (must be a non-empty YYYYMMDD string).
Input string `yaml:"input" validate:"required" doc_example:"birthdate"`
// Output is the target claim name. Defaults to Input.
Output string `yaml:"output,omitempty" doc_example:"birthdate"`
}
YYYYMMDDToISOArgs configures the yyyymmdd_to_iso primitive. Impossible calendar dates surface as a hard error.