diff --git a/docs/config/federation.md b/docs/config/federation.md index c32b256..38d89c9 100644 --- a/docs/config/federation.md +++ b/docs/config/federation.md @@ -249,6 +249,25 @@ The `extra_rp_metadata` option is used to add custom key-value pairs to the rely another_field: another_value ``` +## `extra_fe_metadata` +mapping / object +optional + +The `extra_fe_metadata` option is used to add custom key-value pairs to the `federation_entity` metadata section in the entity configuration. + +This option is applied **before** any automatic copying of informational claims (controlled by [`publish_informational_claims_in_federation_entity`](#publish_informational_claims_in_federation_entity)). This means that values specified in `extra_fe_metadata` take precedence over automatically copied claims. If a field is set in `extra_fe_metadata`, it will not be overwritten by the automatic copying mechanism. + +When `publish_informational_claims_in_federation_entity` is set to `true` (default), OFFA will still automatically copy consistent informational claims from other metadata sections to the federation entity metadata, but only for fields that are not already defined in `extra_fe_metadata`. + +??? file "config.yaml" + + ```yaml + federation: + extra_fe_metadata: + custom_federation_field: custom_value + another_federation_field: another_value + ``` + ## `extra_entity_configuration_data` mapping / object optional diff --git a/internal/config/config.go b/internal/config/config.go index 9707da8..f6f88c5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -78,6 +78,7 @@ type federationConf struct { OrganizationName string `yaml:"organization_name"` OrganizationURI string `yaml:"organization_uri"` ExtraRPMetadata map[string]any `yaml:"extra_rp_metadata"` + ExtraFEMetadata map[string]any `yaml:"extra_fe_metadata"` ExtraEntityConfigurationData map[string]any `yaml:"extra_entity_configuration_data"` ConfigurationLifetime duration.DurationOption `yaml:"configuration_lifetime"` diff --git a/internal/server/server.go b/internal/server/server.go index 5829642..00326b0 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "reflect" "strings" "time" @@ -95,6 +96,9 @@ func initFederationEntity() { OrganizationURI: fedConfig.OrganizationURI, }, } + if fedConfig.ExtraFEMetadata != nil && len(fedConfig.ExtraFEMetadata) > 0 { + metadata.FederationEntity = applyExtraFEMetadata(fedConfig.ExtraFEMetadata) + } if metadata.RelyingParty.Extra == nil { metadata.RelyingParty.Extra = make(map[string]any) } @@ -195,3 +199,47 @@ func getFullPath(path string) string { } return config.Get().Server.Basepath + path } + +func applyExtraFEMetadata(extraFE map[string]any) *oidfed.FederationEntityMetadata { + fe := &oidfed.FederationEntityMetadata{} + v := reflect.ValueOf(fe).Elem() + t := v.Type() + + jsonTagToField := make(map[string]string) + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + jsonTag := field.Tag.Get("json") + if jsonTag != "" && jsonTag != "-" { + if idx := strings.Index(jsonTag, ","); idx != -1 { + jsonTag = jsonTag[:idx] + } + jsonTagToField[jsonTag] = field.Name + } + } + + for key, value := range extraFE { + if fieldName, ok := jsonTagToField[key]; ok { + field := v.FieldByName(fieldName) + if field.IsValid() && field.CanSet() { + switch field.Kind() { + case reflect.String: + if strVal, ok := value.(string); ok { + field.SetString(strVal) + } + case reflect.Slice: + if sliceVal, ok := value.([]interface{}); ok { + strSlice := make([]string, len(sliceVal)) + for i, v := range sliceVal { + if str, ok := v.(string); ok { + strSlice[i] = str + } + } + field.Set(reflect.ValueOf(strSlice)) + } + } + } + } + } + + return fe +}