Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 154 additions & 1 deletion trustchain.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package oidfed

import (
"reflect"

"github.com/pkg/errors"
"github.com/vmihailenco/msgpack/v5"
"github.com/zachmann/go-utils/maputils"
Expand Down Expand Up @@ -89,9 +91,16 @@ func (c TrustChain) Metadata() (*Metadata, error) {
if err != nil {
return nil, err
}
metadataFromSuperior := c[1].Metadata
m := c[0].Metadata
if m == nil {
m = &Metadata{}
if metadataFromSuperior == nil {
m = &Metadata{}
} else {
m = metadataFromSuperior
}
} else if metadataFromSuperior != nil {
mergeMetadata(m, metadataFromSuperior)
}
final, err := m.ApplyPolicy(combinedPolicy)
if err != nil {
Expand All @@ -103,6 +112,150 @@ func (c TrustChain) Metadata() (*Metadata, error) {
return final, nil
}

// mergeMetadata merges values from source into target, with source values taking precedence.
// Any value set in source will overwrite the corresponding value in target.
func mergeMetadata(target, source *Metadata) {
if source == nil {
return
}

targetVal := reflect.ValueOf(target).Elem()
sourceVal := reflect.ValueOf(source).Elem()
typ := targetVal.Type()

// Iterate through all fields of Metadata struct
for i := 0; i < targetVal.NumField(); i++ {
fieldName := typ.Field(i).Name

// Skip the Extra field as it needs special handling
if fieldName == "Extra" {
continue
}

targetField := targetVal.Field(i)
sourceField := sourceVal.Field(i)

// Only proceed if source field is not nil
if sourceField.Kind() == reflect.Ptr && !sourceField.IsNil() {
if targetField.IsNil() {
// If target field is nil, just copy the source field
targetField.Set(sourceField)
} else {
// Both fields are non-nil pointers to structs, merge their values
mergeStructFields(targetField.Elem(), sourceField.Elem())
}
}
}

// Handle Extra field separately
if source.Extra != nil {
if target.Extra == nil {
target.Extra = make(map[string]any)
}
for k, v := range source.Extra {
target.Extra[k] = v
}
}
}

// mergeStructFields merges values from source struct into target struct using reflection.
// Any field set in source will overwrite the corresponding field in target.
func mergeStructFields(target, source reflect.Value) {
// Get the wasSet map from source if it exists
var wasSetMap map[string]bool
if wasSetField := source.FieldByName("wasSet"); wasSetField.IsValid() && wasSetField.CanInterface() {
if m, ok := wasSetField.Interface().(map[string]bool); ok {
wasSetMap = m
}
}

// Get the wasSet map from target if it exists
var targetWasSetMap map[string]bool
if targetWasSetField := target.FieldByName("wasSet"); targetWasSetField.IsValid() && targetWasSetField.CanInterface() {
if m, ok := targetWasSetField.Interface().(map[string]bool); ok {
targetWasSetMap = m
}
} else if targetWasSetField = target.FieldByName("wasSet"); targetWasSetField.IsValid() && targetWasSetField.CanSet() {
// If target has a wasSet field but it's nil, initialize it
newMap := make(map[string]bool)
targetWasSetField.Set(reflect.ValueOf(newMap))
targetWasSetMap = newMap
}

typ := source.Type()
// Iterate through all fields of the struct
for i := 0; i < source.NumField(); i++ {
field := typ.Field(i)
fieldName := field.Name

// Skip the wasSet field
if fieldName == "wasSet" {
continue
}

sourceField := source.Field(i)
targetField := target.FieldByName(fieldName)

// If field doesn't exist in target or can't be set, skip it
if !targetField.IsValid() || !targetField.CanSet() {
continue
}

// Check if this field was explicitly set in source
fieldWasSet := wasSetMap == nil || wasSetMap[fieldName]

// Only overwrite if the field was set in source
if fieldWasSet && !sourceField.IsZero() {
// Handle different field types
if sourceField.Kind() == reflect.Map && targetField.Kind() == reflect.Map {
// For maps, merge the contents
if targetField.IsNil() {
targetField.Set(reflect.MakeMap(targetField.Type()))
}

for _, key := range sourceField.MapKeys() {
value := sourceField.MapIndex(key)
targetField.SetMapIndex(key, value)
}
} else {
// For other types, just copy the value
targetField.Set(sourceField)
}

// Update the wasSet map in target if it exists
if targetWasSetMap != nil {
targetWasSetMap[fieldName] = true
}
}
}

// Handle Extra field separately if it exists
extraField := source.FieldByName("Extra")
if extraField.IsValid() && !extraField.IsNil() {
targetExtraField := target.FieldByName("Extra")
if targetExtraField.IsValid() {
if targetExtraField.IsNil() {
targetExtraField.Set(reflect.MakeMap(targetExtraField.Type()))
}

// Copy all keys from source.Extra to target.Extra
for _, key := range extraField.MapKeys() {
value := extraField.MapIndex(key)
targetExtraField.SetMapIndex(key, value)
}

// Mark these fields as set in wasSet if needed
if targetWasSetMap != nil {
for _, key := range extraField.MapKeys() {
if k, ok := key.Interface().(string); ok {
targetWasSetMap[k] = true
}
}
}
}
}
}

// Messages returns the jwts of the TrustChain
func (c TrustChain) Messages() (msgs JWSMessages) {
for _, cc := range c {
Expand Down
Loading