diff --git a/gql.go b/gql.go index b184031..2cb4158 100644 --- a/gql.go +++ b/gql.go @@ -785,6 +785,14 @@ type GQLExt struct { SubscribeListeners []chan Signal } +func (ext *GQLExt) Field(name string) interface{} { + return ResolveFields(ext, name, map[string]func(*GQLExt)interface{}{ + "listen": func(ext *GQLExt) interface{} { + return ext.Listen + }, + }) +} + func (ext *GQLExt) NewSubscriptionChannel(buffer int) chan Signal { ext.SubscribeLock.Lock() defer ext.SubscribeLock.Unlock() diff --git a/graph_test.go b/graph_test.go index 4a6a9d9..dc620ff 100644 --- a/graph_test.go +++ b/graph_test.go @@ -91,7 +91,7 @@ func NewSimpleListener(ctx *Context, buffer int) (*Node, *ListenerExt) { SimpleListenerNodeType, nil, listener_extension, - NewACLExt(&policy), + NewACLExt(policy), NewLockableExt()) return listener, listener_extension diff --git a/lockable.go b/lockable.go index b3d6650..bc50feb 100644 --- a/lockable.go +++ b/lockable.go @@ -19,6 +19,17 @@ func NewListenerExt(buffer int) *ListenerExt { } } +func (ext *ListenerExt) Field(name string) interface{} { + return ResolveFields(ext, name, map[string]func(*ListenerExt)interface{}{ + "buffer": func(ext *ListenerExt) interface{} { + return ext.Buffer + }, + "chan": func(ext *ListenerExt) interface{} { + return ext.Chan + }, + }) +} + // Simple load function, unmarshal the buffer int from json func LoadListenerExt(ctx *Context, data []byte) (Extension, error) { var j int @@ -66,6 +77,23 @@ type LockableExtJSON struct { Dependencies map[string]string `json:"dependencies"` } +func (ext *LockableExt) Field(name string) interface{} { + return ResolveFields(ext, name, map[string]func(*LockableExt)interface{}{ + "owner": func(ext *LockableExt) interface{} { + return ext.Owner + }, + "pending_owner": func(ext *LockableExt) interface{} { + return ext.PendingOwner + }, + "requirements": func(ext *LockableExt) interface{} { + return ext.Requirements + }, + "dependencies": func(ext *LockableExt) interface{} { + return ext.Dependencies + }, + }) +} + func LoadLockableExt(ctx *Context, data []byte) (Extension, error) { var j LockableExtJSON err := json.Unmarshal(data, &j) diff --git a/lockable_test.go b/lockable_test.go index aef88c8..d931d2c 100644 --- a/lockable_test.go +++ b/lockable_test.go @@ -26,13 +26,13 @@ func TestLink(t *testing.T) { l1_listener := NewListenerExt(10) l1 := NewNode(ctx, RandID(), TestLockableType, nil, l1_listener, - NewACLExt(&link_policy), + NewACLExt(link_policy), NewLockableExt(), ) l2_listener := NewListenerExt(10) l2 := NewNode(ctx, RandID(), TestLockableType, nil, l2_listener, - NewACLExt(&link_policy), + NewACLExt(link_policy), NewLockableExt(), ) @@ -57,7 +57,7 @@ func TestLock(t *testing.T) { listener := NewListenerExt(10) l := NewNode(ctx, RandID(), TestLockableType, nil, listener, - NewACLExt(&lock_policy, &link_policy), + NewACLExt(lock_policy, link_policy), NewLockableExt(), ) return l, listener diff --git a/node.go b/node.go index f39eadf..89c098d 100644 --- a/node.go +++ b/node.go @@ -72,6 +72,7 @@ type Serializable[I comparable] interface { // Extensions are data attached to nodes that process signals type Extension interface { Serializable[ExtType] + Field(string)interface{} Process(context *Context, source NodeID, node *Node, signal Signal) } @@ -142,6 +143,27 @@ type Msg struct { Signal Signal } +func ReadNodeFields(ctx *Context, self *Node, princ NodeID, reqs map[ExtType][]string)map[ExtType]map[string]interface{} { + exts := map[ExtType]map[string]interface{}{} + for ext_type, field_reqs := range(reqs) { + fields := map[string]interface{}{} + for _, req := range(field_reqs) { + err := Allowed(ctx, princ, MakeAction(ReadSignalType, ext_type, req), self) + if err != nil { + fields[req] = err + } else { + ext, exists := self.Extensions[ext_type] + if exists == false { + fields[req] = fmt.Errorf("%s does not have %s extension", self.ID, ext_type) + } else { + fields[req] = ext.Field(req) + } + } + } + } + return exts +} + // Main Loop for Threads, starts a write context, so cannot be called from a write or read context func nodeLoop(ctx *Context, node *Node) error { started := node.Active.CompareAndSwap(false, true) @@ -171,7 +193,16 @@ func nodeLoop(ctx *Context, node *Node) error { if signal.Type() == StopSignalType { node.Process(ctx, node.ID, NewStatusSignal("stopped", node.ID)) break + } else if signal.Type() == ReadSignalType { + read_signal, ok := signal.(ReadSignal) + if ok == false { + ctx.Log.Logf("signal", "READ_SIGNAL: bad cast %+v", signal) + } else { + fields := ReadNodeFields(ctx, node, source, read_signal.Extensions) + ctx.Log.Logf("test", "READ_RESULT: %+v", fields) + } } + node.Process(ctx, source, signal) } diff --git a/policy.go b/policy.go index 821464a..ab43a0f 100644 --- a/policy.go +++ b/policy.go @@ -22,11 +22,11 @@ type Policy interface { Merge(Policy) Policy } -func (policy *AllNodesPolicy) Allows(principal_id NodeID, action Action, node *Node) error { +func (policy AllNodesPolicy) Allows(principal_id NodeID, action Action, node *Node) error { return policy.Actions.Allows(action) } -func (policy *PerNodePolicy) Allows(principal_id NodeID, action Action, node *Node) error { +func (policy PerNodePolicy) Allows(principal_id NodeID, action Action, node *Node) error { for id, actions := range(policy.NodeActions) { if id != principal_id { continue @@ -36,7 +36,7 @@ func (policy *PerNodePolicy) Allows(principal_id NodeID, action Action, node *No return fmt.Errorf("%s is not in per node policy of %s", principal_id, node.ID) } -func (policy *RequirementOfPolicy) Allows(principal_id NodeID, action Action, node *Node) error { +func (policy RequirementOfPolicy) Allows(principal_id NodeID, action Action, node *Node) error { lockable_ext, err := GetExt[*LockableExt](node) if err != nil { return err @@ -54,7 +54,7 @@ func (policy *RequirementOfPolicy) Allows(principal_id NodeID, action Action, no type RequirementOfPolicy struct { AllNodesPolicy } -func (policy *RequirementOfPolicy) Type() PolicyType { +func (policy RequirementOfPolicy) Type() PolicyType { return RequirementOfPolicyType } @@ -83,20 +83,20 @@ func MergeNodeActions(modified NodeActions, read NodeActions) { } } -func (policy *PerNodePolicy) Merge(p Policy) Policy { - other := p.(*PerNodePolicy) +func (policy PerNodePolicy) Merge(p Policy) Policy { + other := p.(PerNodePolicy) MergeNodeActions(policy.NodeActions, other.NodeActions) return policy } -func (policy *AllNodesPolicy) Merge(p Policy) Policy { - other := p.(*AllNodesPolicy) +func (policy AllNodesPolicy) Merge(p Policy) Policy { + other := p.(AllNodesPolicy) policy.Actions = MergeActions(policy.Actions, other.Actions) return policy } -func (policy *RequirementOfPolicy) Merge(p Policy) Policy { - other := p.(*RequirementOfPolicy) +func (policy RequirementOfPolicy) Merge(p Policy) Policy { + other := p.(RequirementOfPolicy) policy.Actions = MergeActions(policy.Actions, other.Actions) return policy } @@ -190,11 +190,11 @@ type PerNodePolicy struct { NodeActions NodeActions `json:"node_actions"` } -func (policy *PerNodePolicy) Type() PolicyType { +func (policy PerNodePolicy) Type() PolicyType { return PerNodePolicyType } -func (policy *PerNodePolicy) Serialize() ([]byte, error) { +func (policy PerNodePolicy) Serialize() ([]byte, error) { return json.MarshalIndent(policy, "", " ") } @@ -212,11 +212,11 @@ type AllNodesPolicy struct { Actions Actions } -func (policy *AllNodesPolicy) Type() PolicyType { +func (policy AllNodesPolicy) Type() PolicyType { return AllNodesPolicyType } -func (policy *AllNodesPolicy) Serialize() ([]byte, error) { +func (policy AllNodesPolicy) Serialize() ([]byte, error) { return json.MarshalIndent(policy, "", " ") } @@ -288,6 +288,14 @@ func (ext *ACLExt) Serialize() ([]byte, error) { func (ext *ACLExt) Process(ctx *Context, princ_id NodeID, node *Node, signal Signal) { } +func (ext *ACLExt) Field(name string) interface{} { + return ResolveFields(ext, name, map[string]func(*ACLExt)interface{}{ + "policies": func(ext *ACLExt) interface{} { + return ext.Policies + }, + }) +} + func NewACLExt(policies ...Policy) *ACLExt { policy_map := map[PolicyType]Policy{} for _, policy := range(policies) { diff --git a/signal.go b/signal.go index 54fb462..2c8e52c 100644 --- a/signal.go +++ b/signal.go @@ -9,6 +9,7 @@ const ( StatusSignalType = SignalType("STATUS") LinkSignalType = SignalType("LINK") LockSignalType = SignalType("LOCK") + ReadSignalType = SignalType("READ") ) type SignalDirection int @@ -144,14 +145,14 @@ func (signal StateSignal) Permission() Action { return MakeAction(signal.Type(), signal.State) } -type StartChildSignal struct { - IDSignal - Action string `json:"action"` +type ReadSignal struct { + BaseSignal + Extensions map[ExtType][]string `json:"extensions"` } -func NewStartChildSignal(child_id NodeID, action string) StartChildSignal { - return StartChildSignal{ - IDSignal: NewIDSignal("start_child", Direct, child_id), - Action: action, +func NewReadSignal(exts map[ExtType][]string) ReadSignal { + return ReadSignal{ + BaseSignal: NewDirectSignal(ReadSignalType), + Extensions: exts, } } diff --git a/user.go b/user.go index de36048..f2dc16f 100644 --- a/user.go +++ b/user.go @@ -14,6 +14,29 @@ type ECDHExt struct { Shared []byte } +func ResolveFields[T Extension](t T, name string, field_funcs map[string]func(T)interface{})interface{} { + var zero T + field_func, ok := field_funcs[name] + if ok == false { + return fmt.Errorf("%s is not a field of %s", name, zero.Type()) + } + return field_func(t) +} + +func (ext *ECDHExt) Field(name string) interface{} { + return ResolveFields(ext, name, map[string]func(*ECDHExt)interface{}{ + "granted": func(ext *ECDHExt) interface{} { + return ext.Granted + }, + "pubkey": func(ext *ECDHExt) interface{} { + return ext.Pubkey + }, + "shared": func(ext *ECDHExt) interface{} { + return ext.Shared + }, + }) +} + type ECDHExtJSON struct { Granted time.Time `json:"granted"` Pubkey []byte `json:"pubkey"` @@ -88,6 +111,14 @@ func (ext *GroupExt) Serialize() ([]byte, error) { }, "", " ") } +func (ext *GroupExt) Field(name string) interface{} { + return ResolveFields(ext, name, map[string]func(*GroupExt)interface{}{ + "members": func(ext *GroupExt) interface{} { + return ext.Members + }, + }) +} + func NewGroupExt(members map[NodeID]string) *GroupExt { if members == nil { members = map[NodeID]string{}