Added ParentOfPolicy and ChildOfPolicy

graph-rework-2
noah metz 2023-07-26 13:28:03 -06:00
parent fa6142d880
commit 755edf8558
4 changed files with 371 additions and 73 deletions

@ -11,14 +11,30 @@ import (
) )
func TestGQLDBLoad(t * testing.T) { func TestGQLDBLoad(t * testing.T) {
ctx := logTestContext(t, []string{"test", "db"}) ctx := logTestContext(t, []string{"test", "db", "policy", "signal"})
TestUserNodeType := NodeType("TEST_USER")
err := ctx.RegisterNodeType(TestUserNodeType, []ExtType{ACLExtType, ACLPolicyExtType})
fatalErr(t, err)
u1 := NewNode(RandID(), TestUserNodeType)
ctx.Nodes[u1.ID] = &u1
u1.Extensions[ACLExtType] = NewACLExt(nil)
u1.Extensions[ACLPolicyExtType] = NewACLPolicyExt(map[PolicyType]Policy{
PerNodePolicyType: NewPerNodePolicy(map[NodeID][]string{
u1.ID: []string{"users.write", "children.write", "parent.write", "dependencies.write", "requirements.write"},
}, nil),
})
ctx.Log.Logf("test", "U1_ID: %s", u1.ID)
ListenerNodeType := NodeType("LISTENER") ListenerNodeType := NodeType("LISTENER")
err := ctx.RegisterNodeType(ListenerNodeType, []ExtType{ACLExtType, ListenerExtType, LockableExtType}) err = ctx.RegisterNodeType(ListenerNodeType, []ExtType{ACLExtType, ListenerExtType, LockableExtType})
fatalErr(t, err) fatalErr(t, err)
l1 := NewNode(RandID(), ListenerNodeType) l1 := NewNode(RandID(), ListenerNodeType)
l1.Extensions[ACLExtType] = NewACLExt(nil) ctx.Nodes[l1.ID] = &l1
l1.Extensions[ACLExtType] = NewACLExt(NodeList(&u1))
listener_ext := NewListenerExt(10) listener_ext := NewListenerExt(10)
l1.Extensions[ListenerExtType] = listener_ext l1.Extensions[ListenerExtType] = listener_ext
l1.Extensions[LockableExtType] = NewLockableExt(nil, nil, nil, nil) l1.Extensions[LockableExtType] = NewLockableExt(nil, nil, nil, nil)
@ -30,22 +46,19 @@ func TestGQLDBLoad(t * testing.T) {
fatalErr(t, err) fatalErr(t, err)
t1 := NewNode(RandID(), TestThreadNodeType) t1 := NewNode(RandID(), TestThreadNodeType)
t1.Extensions[ACLExtType] = NewACLExt(nil) ctx.Nodes[t1.ID] = &t1
t1.Extensions[ACLExtType] = NewACLExt(NodeList(&u1))
t1.Extensions[ACLPolicyExtType] = NewACLPolicyExt(map[PolicyType]Policy{
ParentOfPolicyType: NewParentOfPolicy(map[NodeID][]string{
t1.ID: []string{"signal.abort", "state.write"},
}),
})
t1.Extensions[ThreadExtType], err = NewThreadExt(ctx, BaseThreadType, nil, nil, "init", nil) t1.Extensions[ThreadExtType], err = NewThreadExt(ctx, BaseThreadType, nil, nil, "init", nil)
fatalErr(t, err) fatalErr(t, err)
t1.Extensions[LockableExtType] = NewLockableExt(nil, nil, nil, nil) t1.Extensions[LockableExtType] = NewLockableExt(nil, nil, nil, nil)
ctx.Log.Logf("test", "T1_ID: %s", t1.ID) ctx.Log.Logf("test", "T1_ID: %s", t1.ID)
TestUserNodeType := NodeType("TEST_USER")
err = ctx.RegisterNodeType(TestUserNodeType, []ExtType{ACLExtType})
fatalErr(t, err)
u1 := NewNode(RandID(), TestUserNodeType)
u1.Extensions[ACLExtType] = NewACLExt(nil)
ctx.Log.Logf("test", "U1_ID: %s", u1.ID)
TestGQLNodeType := NodeType("TEST_GQL") TestGQLNodeType := NodeType("TEST_GQL")
err = ctx.RegisterNodeType(TestGQLNodeType, []ExtType{ACLExtType, GroupExtType, GQLExtType, ThreadExtType, LockableExtType}) err = ctx.RegisterNodeType(TestGQLNodeType, []ExtType{ACLExtType, GroupExtType, GQLExtType, ThreadExtType, LockableExtType})
fatalErr(t, err) fatalErr(t, err)
@ -54,7 +67,13 @@ func TestGQLDBLoad(t * testing.T) {
fatalErr(t, err) fatalErr(t, err)
gql := NewNode(RandID(), TestGQLNodeType) gql := NewNode(RandID(), TestGQLNodeType)
gql.Extensions[ACLExtType] = NewACLExt(nil) ctx.Nodes[gql.ID] = &gql
gql.Extensions[ACLExtType] = NewACLExt(NodeList(&u1))
gql.Extensions[ACLPolicyExtType] = NewACLPolicyExt(map[PolicyType]Policy{
ChildOfPolicyType: NewChildOfPolicy(map[NodeID][]string{
gql.ID: []string{"signal.status"},
}),
})
gql.Extensions[GroupExtType] = NewGroupExt(nil) gql.Extensions[GroupExtType] = NewGroupExt(nil)
gql.Extensions[GQLExtType] = NewGQLExt(":0", ecdh.P256(), key, nil, nil) gql.Extensions[GQLExtType] = NewGQLExt(":0", ecdh.P256(), key, nil, nil)
gql.Extensions[ThreadExtType], err = NewThreadExt(ctx, GQLThreadType, nil, nil, "ini", nil) gql.Extensions[ThreadExtType], err = NewThreadExt(ctx, GQLThreadType, nil, nil, "ini", nil)
@ -62,17 +81,16 @@ func TestGQLDBLoad(t * testing.T) {
gql.Extensions[LockableExtType] = NewLockableExt(nil, nil, nil, nil) gql.Extensions[LockableExtType] = NewLockableExt(nil, nil, nil, nil)
ctx.Log.Logf("test", "GQL_ID: %s", gql.ID) ctx.Log.Logf("test", "GQL_ID: %s", gql.ID)
info := ParentInfo{true, "start", "restore"} info := ParentInfo{true, "start", "restore"}
context := NewWriteContext(ctx) context := NewWriteContext(ctx)
err = UpdateStates(context, &gql, NewACLInfo(&gql, []string{"users"}), func(context *StateContext) error { err = UpdateStates(context, &u1, NewACLInfo(&gql, []string{"users"}), func(context *StateContext) error {
err := LinkThreads(context, &gql, &gql, ChildInfo{&t1, map[InfoType]Info{ err := LinkThreads(context, &u1, &gql, ChildInfo{&t1, map[InfoType]Info{
ParentInfoType: &info, ParentInfoType: &info,
}}) }})
if err != nil { if err != nil {
return err return err
} }
return LinkLockables(context, &gql, &l1, []*Node{&gql}) return LinkLockables(context, &u1, &l1, []*Node{&gql})
}) })
fatalErr(t, err) fatalErr(t, err)

@ -150,6 +150,7 @@ func NewNode(id NodeID, node_type NodeType) Node {
} }
func Allowed(context *StateContext, principal *Node, action string, node *Node) error { func Allowed(context *StateContext, principal *Node, action string, node *Node) error {
context.Graph.Log.Logf("policy", "POLICY_CHECK: %s %s.%s", principal.ID, node.ID, action)
if principal == nil { if principal == nil {
context.Graph.Log.Logf("policy", "POLICY_CHECK_ERR: %s %s.%s", principal.ID, node.ID, action) context.Graph.Log.Logf("policy", "POLICY_CHECK_ERR: %s %s.%s", principal.ID, node.ID, action)
return fmt.Errorf("nil is not allowed to perform any actions") return fmt.Errorf("nil is not allowed to perform any actions")
@ -160,24 +161,31 @@ func Allowed(context *StateContext, principal *Node, action string, node *Node)
return nil return nil
} }
// Check if the node has a policy extension itself, and check against the policies in it
policy_ext, err := GetExt[*ACLPolicyExt](node)
if err == nil {
if policy_ext.Allows(context, principal, action, node) == true {
return nil
}
}
acl_ext, err := GetExt[*ACLExt](node) acl_ext, err := GetExt[*ACLExt](node)
if err != nil { if err != nil {
return err return err
} }
for _, policy_node := range(acl_ext.Delegations) { for _, policy_node := range(acl_ext.Delegations) {
ext, exists := policy_node.Extensions[ACLPolicyExtType] context.Graph.Log.Logf("policy", "POLICY_DELEGATION_CHECK: %s->%s", node.ID, policy_node.ID)
if exists == false { policy_ext, err := GetExt[*ACLPolicyExt](policy_node)
context.Graph.Log.Logf("policy", "WARNING: %s has dependency %s which doesn't have ACLPolicyExt") if err != nil {
continue return err
} }
policy_ext := ext.(ACLPolicyExt)
if policy_ext.Allows(context, principal, action, node) == true { if policy_ext.Allows(context, principal, action, node) == true {
context.Graph.Log.Logf("policy", "POLICY_CHECK_PASS: %s %s.%s", principal.ID, node.ID, action) context.Graph.Log.Logf("policy", "POLICY_CHECK_PASS: %s %s.%s", principal.ID, node.ID, action)
return nil return nil
} }
} }
context.Graph.Log.Logf("policy", "POLICY_CHECK_FAIL: %s %s.%s.%s", principal.ID, node.ID, action) context.Graph.Log.Logf("policy", "POLICY_CHECK_FAIL: %s %s.%s", principal.ID, node.ID, action)
return fmt.Errorf("%s is not allowed to perform %s on %s", principal.ID, action, node.ID) return fmt.Errorf("%s is not allowed to perform %s on %s", principal.ID, action, node.ID)
} }
@ -190,10 +198,14 @@ func Signal(context *StateContext, node *Node, princ *Node, signal GraphSignal)
return Allowed(context, princ, fmt.Sprintf("signal.%s", signal.Type()), node) return Allowed(context, princ, fmt.Sprintf("signal.%s", signal.Type()), node)
}) })
if err != nil {
return err
}
for _, ext := range(node.Extensions) { for _, ext := range(node.Extensions) {
err = ext.Process(context, node, signal) err = ext.Process(context, node, signal)
if err != nil { if err != nil {
return nil return err
} }
} }
@ -324,7 +336,7 @@ func WriteNodes(context *StateContext) error {
for id, _ := range(context.Locked) { for id, _ := range(context.Locked) {
node, _ := context.Graph.Nodes[id] node, _ := context.Graph.Nodes[id]
if node == nil { if node == nil {
return fmt.Errorf("DB_SERIALIZE_ERROR: cannot serialize nil node, maybe node isn't in the context") return fmt.Errorf("DB_SERIALIZE_ERROR: cannot serialize nil node(%s), maybe node isn't in the context", id)
} }
ser, err := node.Serialize() ser, err := node.Serialize()

@ -6,31 +6,262 @@ import (
) )
type Policy interface { type Policy interface {
Type() PolicyType
Serialize() ([]byte, error) Serialize() ([]byte, error)
Allows(context *StateContext, principal *Node, action string, node *Node) bool Allows(context *StateContext, principal *Node, action string, node *Node) bool
} }
func LoadAllNodesPolicy(ctx *Context, data []byte) (Policy, error) { const ChildOfPolicyType = PolicyType("CHILD_OF")
var policy AllNodesPolicy type ChildOfPolicy struct {
err := json.Unmarshal(data, &policy) NodeActions map[NodeID][]string
}
func (policy *ChildOfPolicy) Type() PolicyType {
return ChildOfPolicyType
}
func (policy *ChildOfPolicy) Serialize() ([]byte, error) {
node_actions := map[string][]string{}
for id, actions := range(policy.NodeActions) {
node_actions[id.String()] = actions
}
return json.MarshalIndent(&ChildOfPolicyJSON{
NodeActions: node_actions,
}, "", " ")
}
func (policy *ChildOfPolicy) Allows(context *StateContext, principal *Node, action string, node *Node) bool {
context.Graph.Log.Logf("policy", "CHILD_OF_POLICY: %+v", policy)
thread_ext, err := GetExt[*ThreadExt](principal)
if err != nil {
return false
}
parent := thread_ext.Parent
if parent != nil {
actions, exists := policy.NodeActions[parent.ID]
if exists == false {
return false
}
for _, a := range(actions) {
if a == action {
return true
}
}
}
return false
}
type ChildOfPolicyJSON struct {
NodeActions map[string][]string `json:"node_actions"`
}
func LoadChildOfPolicy(ctx *Context, data []byte) (Policy, error) {
var j ChildOfPolicyJSON
err := json.Unmarshal(data, &j)
if err != nil { if err != nil {
return policy, err return nil, err
}
node_actions := map[NodeID][]string{}
for id_str, actions := range(j.NodeActions) {
id, err := ParseID(id_str)
if err != nil {
return nil, err
}
_, err = LoadNode(ctx, id)
if err != nil {
return nil, err
}
node_actions[id] = actions
}
return NewChildOfPolicy(node_actions), nil
}
func NewChildOfPolicy(node_actions map[NodeID][]string) *ChildOfPolicy {
if node_actions == nil {
node_actions = map[NodeID][]string{}
}
return &ChildOfPolicy{
NodeActions: node_actions,
}
}
const ParentOfPolicyType = PolicyType("PARENT_OF")
type ParentOfPolicy struct {
NodeActions map[NodeID][]string
}
func (policy *ParentOfPolicy) Type() PolicyType {
return ParentOfPolicyType
}
func (policy *ParentOfPolicy) Serialize() ([]byte, error) {
node_actions := map[string][]string{}
for id, actions := range(policy.NodeActions) {
node_actions[id.String()] = actions
} }
return policy, nil return json.MarshalIndent(&ParentOfPolicyJSON{
NodeActions: node_actions,
}, "", " ")
} }
type AllNodesPolicy struct { func (policy *ParentOfPolicy) Allows(context *StateContext, principal *Node, action string, node *Node) bool {
Actions []string `json:"actions"` context.Graph.Log.Logf("policy", "PARENT_OF_POLICY: %+v", policy)
for id, actions := range(policy.NodeActions) {
thread_ext, err := GetExt[*ThreadExt](context.Graph.Nodes[id])
if err != nil {
continue
}
context.Graph.Log.Logf("policy", "PARENT_OF_PARENT: %s %+v", id, thread_ext.Parent)
if thread_ext.Parent != nil {
if thread_ext.Parent.ID == principal.ID {
for _, a := range(actions) {
if a == action {
return true
}
}
}
}
}
return false
} }
func (policy AllNodesPolicy) Type() PolicyType { type ParentOfPolicyJSON struct {
return PolicyType("simple_policy") NodeActions map[string][]string `json:"node_actions"`
} }
func (policy AllNodesPolicy) Serialize() ([]byte, error) { func LoadParentOfPolicy(ctx *Context, data []byte) (Policy, error) {
return json.MarshalIndent(&policy, "", " ") var j ParentOfPolicyJSON
err := json.Unmarshal(data, &j)
if err != nil {
return nil, err
}
node_actions := map[NodeID][]string{}
for id_str, actions := range(j.NodeActions) {
id, err := ParseID(id_str)
if err != nil {
return nil, err
}
_, err = LoadNode(ctx, id)
if err != nil {
return nil, err
}
node_actions[id] = actions
}
return NewParentOfPolicy(node_actions), nil
} }
func NewParentOfPolicy(node_actions map[NodeID][]string) *ParentOfPolicy {
if node_actions == nil {
node_actions = map[NodeID][]string{}
}
return &ParentOfPolicy{
NodeActions: node_actions,
}
}
func LoadPerNodePolicy(ctx *Context, data []byte) (Policy, error) {
var j PerNodePolicyJSON
err := json.Unmarshal(data, &j)
if err != nil {
return nil, err
}
node_actions := map[NodeID][]string{}
for id_str, actions := range(j.NodeActions) {
id, err := ParseID(id_str)
if err != nil {
return nil, err
}
_, err = LoadNode(ctx, id)
if err != nil {
return nil, err
}
node_actions[id] = actions
}
return NewPerNodePolicy(node_actions, j.WildcardActions), nil
}
func NewPerNodePolicy(node_actions map[NodeID][]string, wildcard_actions []string) *PerNodePolicy {
if node_actions == nil {
node_actions = map[NodeID][]string{}
}
if wildcard_actions == nil {
wildcard_actions = []string{}
}
return &PerNodePolicy{
NodeActions: node_actions,
WildcardActions: wildcard_actions,
}
}
type PerNodePolicy struct {
NodeActions map[NodeID][]string
WildcardActions []string
}
type PerNodePolicyJSON struct {
NodeActions map[string][]string `json:"node_actions"`
WildcardActions []string `json:"wildcard_actions"`
}
const PerNodePolicyType = PolicyType("PER_NODE")
func (policy PerNodePolicy) Type() PolicyType {
return PerNodePolicyType
}
func (policy PerNodePolicy) Serialize() ([]byte, error) {
node_actions := map[string][]string{}
for id, actions := range(policy.NodeActions) {
node_actions[id.String()] = actions
}
return json.MarshalIndent(&PerNodePolicyJSON{
NodeActions: node_actions,
WildcardActions: policy.WildcardActions,
}, "", " ")
}
func (policy PerNodePolicy) Allows(context *StateContext, principal *Node, action string, node *Node) bool {
for _, a := range(policy.WildcardActions) {
if a == action {
return true
}
}
for id, actions := range(policy.NodeActions) {
if id != principal.ID {
continue
}
for _, a := range(actions) {
if a == action {
return true
}
}
}
return false
}
// Extension to allow a node to hold ACL policies // Extension to allow a node to hold ACL policies
type ACLPolicyExt struct { type ACLPolicyExt struct {
Policies map[PolicyType]Policy Policies map[PolicyType]Policy
@ -65,6 +296,14 @@ func LoadACLExt(ctx *Context, data []byte) (Extension, error) {
}, nil }, nil
} }
func NodeList(nodes ...*Node) NodeMap {
m := NodeMap{}
for _, node := range(nodes) {
m[node.ID] = node
}
return m
}
func NewACLExt(delegations NodeMap) *ACLExt { func NewACLExt(delegations NodeMap) *ACLExt {
if delegations == nil { if delegations == nil {
delegations = NodeMap{} delegations = NodeMap{}
@ -98,7 +337,6 @@ func (ext *ACLExt) Type() ExtType {
type PolicyLoadFunc func(*Context, []byte) (Policy, error) type PolicyLoadFunc func(*Context, []byte) (Policy, error)
type PolicyInfo struct { type PolicyInfo struct {
Load PolicyLoadFunc Load PolicyLoadFunc
Type PolicyType
} }
type ACLPolicyExtContext struct { type ACLPolicyExtContext struct {
@ -106,10 +344,22 @@ type ACLPolicyExtContext struct {
} }
func NewACLPolicyExtContext() *ACLPolicyExtContext { func NewACLPolicyExtContext() *ACLPolicyExtContext {
return nil return &ACLPolicyExtContext{
Types: map[PolicyType]PolicyInfo{
PerNodePolicyType: PolicyInfo{
Load: LoadPerNodePolicy,
},
ParentOfPolicyType: PolicyInfo{
Load: LoadParentOfPolicy,
},
ChildOfPolicyType: PolicyInfo{
Load: LoadChildOfPolicy,
},
},
}
} }
func (ext ACLPolicyExt) Serialize() ([]byte, error) { func (ext *ACLPolicyExt) Serialize() ([]byte, error) {
policies := map[string][]byte{} policies := map[string][]byte{}
for name, policy := range(ext.Policies) { for name, policy := range(ext.Policies) {
ser, err := policy.Serialize() ser, err := policy.Serialize()
@ -126,10 +376,20 @@ func (ext ACLPolicyExt) Serialize() ([]byte, error) {
}, "", " ") }, "", " ")
} }
func (ext ACLPolicyExt) Process(context *StateContext, node *Node, signal GraphSignal) error { func (ext *ACLPolicyExt) Process(context *StateContext, node *Node, signal GraphSignal) error {
return nil return nil
} }
func NewACLPolicyExt(policies map[PolicyType]Policy) *ACLPolicyExt {
if policies == nil {
policies = map[PolicyType]Policy{}
}
return &ACLPolicyExt{
Policies: policies,
}
}
func LoadACLPolicyExt(ctx *Context, data []byte) (Extension, error) { func LoadACLPolicyExt(ctx *Context, data []byte) (Extension, error) {
var j struct { var j struct {
Policies map[string][]byte `json:"policies"` Policies map[string][]byte `json:"policies"`
@ -154,32 +414,22 @@ func LoadACLPolicyExt(ctx *Context, data []byte) (Extension, error) {
policies[PolicyType(name)] = policy policies[PolicyType(name)] = policy
} }
return ACLPolicyExt{ return NewACLPolicyExt(policies), nil
Policies: policies,
}, nil
} }
const ACLPolicyExtType = ExtType("ACL_POLICIES") const ACLPolicyExtType = ExtType("ACL_POLICIES")
func (ext ACLPolicyExt) Type() ExtType { func (ext *ACLPolicyExt) Type() ExtType {
return ACLPolicyExtType return ACLPolicyExtType
} }
// Check if the extension allows the principal to perform action on node // Check if the extension allows the principal to perform action on node
func (ext ACLPolicyExt) Allows(context *StateContext, principal *Node, action string, node *Node) bool { func (ext *ACLPolicyExt) Allows(context *StateContext, principal *Node, action string, node *Node) bool {
context.Graph.Log.Logf("policy", "POLICY_EXT_ALLOWED: %+v", ext)
for _, policy := range(ext.Policies) { for _, policy := range(ext.Policies) {
context.Graph.Log.Logf("policy", "POLICY_CHECK_POLICY: %+v", policy)
if policy.Allows(context, principal, action, node) == true { if policy.Allows(context, principal, action, node) == true {
return true return true
} }
} }
return false return false
} }
func (policy AllNodesPolicy) Allows(context *StateContext, principal *Node, action string, node *Node) bool {
for _, a := range(policy.Actions) {
if a == action {
return true
}
}
return false
}

@ -142,12 +142,31 @@ type ThreadExtJSON struct {
State string `json:"state"` State string `json:"state"`
Type string `json:"type"` Type string `json:"type"`
Parent string `json:"parent"` Parent string `json:"parent"`
Children map[string][]byte `json:"children"` Children map[string]map[string][]byte `json:"children"`
ActionQueue []QueuedAction ActionQueue []QueuedAction
} }
func (ext *ThreadExt) Serialize() ([]byte, error) { func (ext *ThreadExt) Serialize() ([]byte, error) {
return nil, fmt.Errorf("NOT_IMPLEMENTED") children := map[string]map[string][]byte{}
for id, child := range(ext.Children) {
id_str := id.String()
children[id_str] = map[string][]byte{}
for info_type, info := range(child.Infos) {
var err error
children[id_str][string(info_type)], err = info.Serialize()
if err != nil {
return nil, err
}
}
}
return json.MarshalIndent(&ThreadExtJSON{
State: ext.State,
Type: string(ext.ThreadType),
Parent: SaveNode(ext.Parent),
Children: children,
ActionQueue: ext.ActionQueue,
}, "", " ")
} }
func NewThreadExt(ctx*Context, thread_type ThreadType, parent *Node, children map[NodeID]ChildInfo, state string, action_queue []QueuedAction) (*ThreadExt, error) { func NewThreadExt(ctx*Context, thread_type ThreadType, parent *Node, children map[NodeID]ChildInfo, state string, action_queue []QueuedAction) (*ThreadExt, error) {
@ -265,10 +284,11 @@ func (ext *ThreadExt) Process(context *StateContext, node *Node, signal GraphSig
case Up: case Up:
err = UseStates(context, node, NewACLInfo(node, []string{"parent"}), func(context *StateContext) error { err = UseStates(context, node, NewACLInfo(node, []string{"parent"}), func(context *StateContext) error {
if ext.Parent != nil { if ext.Parent != nil {
return Signal(context, ext.Parent, node, signal) if ext.Parent.ID != node.ID {
} else { return Signal(context, ext.Parent, node, signal)
return nil }
} }
return nil
}) })
case Down: case Down:
err = UseStates(context, node, NewACLInfo(node, []string{"children"}), func(context *StateContext) error { err = UseStates(context, node, NewACLInfo(node, []string{"children"}), func(context *StateContext) error {
@ -360,7 +380,7 @@ func LinkThreads(context *StateContext, principal *Node, thread *Node, info Chil
return err return err
} }
child_ext, err := GetExt[*ThreadExt](thread) child_ext, err := GetExt[*ThreadExt](child)
if err != nil { if err != nil {
return err return err
} }
@ -562,17 +582,15 @@ func ThreadStartChild(ctx *Context, thread *Node, thread_ext *ThreadExt, signal
if exists == false { if exists == false {
return fmt.Errorf("%s is not a child of %s", sig.ID, thread.ID) return fmt.Errorf("%s is not a child of %s", sig.ID, thread.ID)
} }
return UpdateStates(context, thread, NewACLInfo(info.Child, []string{"start"}), func(context *StateContext) error {
parent_info, exists := info.Infos["parent"].(*ParentInfo) parent_info, exists := info.Infos["parent"].(*ParentInfo)
if exists == false { if exists == false {
return fmt.Errorf("Called ThreadStartChild from a thread that doesn't require parent child info") return fmt.Errorf("Called ThreadStartChild from a thread that doesn't require parent child info")
} }
parent_info.Start = true parent_info.Start = true
ChildGo(ctx, thread_ext, info.Child, sig.Action) ChildGo(ctx, thread_ext, info.Child, sig.Action)
return nil return nil
})
}) })
} }
@ -581,14 +599,14 @@ func ThreadStartChild(ctx *Context, thread *Node, thread_ext *ThreadExt, signal
func ThreadRestore(ctx * Context, thread *Node, thread_ext *ThreadExt, start bool) error { func ThreadRestore(ctx * Context, thread *Node, thread_ext *ThreadExt, start bool) error {
context := NewWriteContext(ctx) context := NewWriteContext(ctx)
return UpdateStates(context, thread, NewACLInfo(thread, []string{"children"}), func(context *StateContext) error { return UpdateStates(context, thread, NewACLInfo(thread, []string{"children"}), func(context *StateContext) error {
return UpdateStates(context, thread, ACLList(thread_ext.ChildList(), []string{"start", "state"}), func(context *StateContext) error { return UpdateStates(context, thread, ACLList(thread_ext.ChildList(), []string{"state"}), func(context *StateContext) error {
for _, info := range(thread_ext.Children) { for _, info := range(thread_ext.Children) {
child_ext, err := GetExt[*ThreadExt](info.Child) child_ext, err := GetExt[*ThreadExt](info.Child)
if err != nil { if err != nil {
return err return err
} }
parent_info := info.Infos["parent"].(*ParentInfo) parent_info := info.Infos[ParentInfoType].(*ParentInfo)
if parent_info.Start == true && child_ext.State != "finished" { if parent_info.Start == true && child_ext.State != "finished" {
ctx.Log.Logf("thread", "THREAD_RESTORED: %s -> %s", thread.ID, info.Child.ID) ctx.Log.Logf("thread", "THREAD_RESTORED: %s -> %s", thread.ID, info.Child.ID)
if start == true { if start == true {