okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
02d2013
main
1package daemon 2 3import ( 4"encoding/json" 5"errors" 6"fmt" 7"strconv" 8"strings" 9 10"albedo/cli/internal/daemon/protocol" 11) 12 13// Binding keys are canonical JSON pointers. Reject ancestor assignments before 14// resolving optional values so omission cannot make an ambiguous form valid. 15func validateBodyBindings (bindings map [string ]protocol.Binding )error { 16for pointer := range bindings { 17if ! strings .HasPrefix (pointer ,"/" ) { 18return errors .New ("body binding keys must be JSON pointers" ) 19 } 20for i := 0 ;i < len (pointer );i ++ { 21switch pointer [i ] { 22case '~' : 23if i + 1 == len (pointer )|| pointer [i + 1 ]!= '0' && pointer [i + 1 ]!= '1' { 24return fmt .Errorf ("invalid JSON pointer escape in %q" ,pointer ) 25 } 26i ++ 27case '/' : 28if i > 0 { 29if _ ,exists := bindings [pointer [:i ]];exists { 30return errors .New ("overlapping body bindings" ) 31 } 32 } 33 } 34 } 35 } 36return nil 37} 38 39func bindingText (raw json.RawMessage ) (string ,error ) { 40var text string 41if json .Unmarshal (raw ,& text )== nil { 42return text ,nil 43 } 44var scalar any 45if json .Unmarshal (raw ,& scalar )!= nil { 46return "" ,errors .New ("invalid binding value" ) 47 } 48switch scalar .(type ) { 49case bool ,float64 : 50return string (raw ),nil 51 } 52return "" ,errors .New ("path, query, and header bindings require scalar values" ) 53} 54func resolveBinding (raw ,row ,form ,session json.RawMessage ) (json.RawMessage ,error ) { 55var binding struct { 56Source string `json:"source"` 57Value json.RawMessage `json:"value"` 58Pointer string `json:"pointer"` 59 } 60if err := json .Unmarshal (raw ,& binding );err != nil { 61return nil ,err 62 } 63switch binding .Source { 64case "literal" : 65if binding .Value == nil { 66return nil ,errors .New ("literal binding requires a value" ) 67 } 68return binding .Value ,nil 69case "row" : 70raw = row 71case "form" : 72raw = form 73case "session" : 74raw = session 75default : 76return nil ,errors .New ("unknown binding source" ) 77 } 78if raw == nil { 79return nil ,errors .New ("action requires its displayed row or session" ) 80 } 81if binding .Pointer == "" { 82return raw ,nil 83 } 84if ! strings .HasPrefix (binding .Pointer ,"/" ) { 85return nil ,errors .New ("invalid JSON pointer" ) 86 } 87for token := range strings .SplitSeq (binding .Pointer [1 :],"/" ) { 88token = strings .ReplaceAll (strings .ReplaceAll (token ,"~1" ,"/" ),"~0" ,"~" ) 89if len (raw )> 0 && raw [0 ]== '[' { 90var array []json.RawMessage 91if err := json .Unmarshal (raw ,& array );err != nil { 92return nil ,err 93 } 94index ,err := strconv .Atoi (token ) 95if err != nil || index < 0 || index >= len (array ) { 96return nil ,errors .New ("binding array index is missing" ) 97 } 98raw = array [index ] 99 }else { 100var object map [string ]json.RawMessage 101if err := json .Unmarshal (raw ,& object );err != nil { 102return nil ,errors .New ("binding pointer does not name an object field" ) 103 } 104var exists bool 105raw ,exists = object [token ] 106if ! exists { 107if binding .Source == "form" { 108return nil ,& missingFormBindingError {Field :token } 109 } 110return nil ,fmt .Errorf ("binding field %q is missing" ,token ) 111 } 112 } 113 } 114return raw ,nil 115} 116func setBodyPointer (root map [string ]any ,pointer string ,value any )error { 117if ! strings .HasPrefix (pointer ,"/" ) { 118return errors .New ("body binding keys must be JSON pointers" ) 119 } 120tokens := strings .Split (pointer [1 :],"/" ) 121current := root 122for i ,token := range tokens { 123token = strings .ReplaceAll (strings .ReplaceAll (token ,"~1" ,"/" ),"~0" ,"~" ) 124if i == len (tokens )- 1 { 125current [token ]= value 126return nil 127 } 128child ,ok := current [token ].(map [string ]any ) 129if ! ok { 130if _ ,exists := current [token ];exists { 131return errors .New ("overlapping body bindings" ) 132 } 133child = map [string ]any {} 134current [token ]= child 135 } 136current = child 137 } 138return nil 139} 140 141type missingFormBindingError struct {Field string } 142 143func (e * missingFormBindingError )Error ()string {return "form field " + e .Field + " is omitted" }