From 4b7bc9391413d512d2bf2d39e9e3df9be0de9e92 Mon Sep 17 00:00:00 2001 From: Noah Metz Date: Sun, 15 Oct 2023 15:14:33 -0600 Subject: [PATCH] Moved groups to use SubGroups instead so one node can support many sub_groups(admin, ref, user, etc.) to reduce signals sent --- acl_test.go | 33 +++++-- context.go | 10 ++ gql.go | 64 +++++++++++-- gql_test.go | 26 +++--- group.go | 230 ++++++++++++++++++++++++++++++++++++++++++---- group_test.go | 53 ++++++++--- lockable.go | 44 +++++++++ policy.go | 115 ----------------------- serialize.go | 32 ++++--- serialize_test.go | 6 ++ 10 files changed, 425 insertions(+), 188 deletions(-) diff --git a/acl_test.go b/acl_test.go index 713207d..68767c3 100644 --- a/acl_test.go +++ b/acl_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" "reflect" + "runtime/debug" ) func checkSignal[S Signal](t *testing.T, signal Signal, check func(S)){ @@ -11,6 +12,7 @@ func checkSignal[S Signal](t *testing.T, signal Signal, check func(S)){ if cast_ok == false { error_signal, is_error := signal.(*ErrorSignal) if is_error { + t.Log(string(debug.Stack())) t.Fatal(error_signal.Error) } t.Fatalf("Response of wrong type %s", reflect.TypeOf(signal)) @@ -54,7 +56,7 @@ func testSend(t *testing.T, ctx *Context, signal Signal, source, destination *No } func TestACLBasic(t *testing.T) { - ctx := logTestContext(t, []string{"test", "acl"}) + ctx := logTestContext(t, []string{"test", "acl", "policy"}) listener, err := NewNode(ctx, nil, BaseNodeType, 100, nil, NewListenerExt(100)) fatalErr(t, err) @@ -68,24 +70,33 @@ func TestACLBasic(t *testing.T) { NewPerNodePolicy(map[NodeID]Tree{ listener.ID: { SerializedType(AddMemberSignalType): nil, + SerializedType(AddSubGroupSignalType): nil, }, }), }, NewGroupExt(nil)) fatalErr(t, err) testSendACL(t, ctx, listener, nil, []Policy{ - NewMemberOfPolicy(map[NodeID]Tree{ - group.ID: nil, + NewMemberOfPolicy(map[NodeID]map[string]Tree{ + group.ID: { + "test_group": nil, + }, }), }, testErrorSignal(t, "acl_denied")) - add_member_signal := NewAddMemberSignal(listener.ID) + add_subgroup_signal := NewAddSubGroupSignal("test_group") + add_subgroup_response := testSend(t, ctx, add_subgroup_signal, listener, group) + checkSignal(t, add_subgroup_response, testSuccess) + + add_member_signal := NewAddMemberSignal("test_group", listener.ID) add_member_response := testSend(t, ctx, add_member_signal, listener, group) checkSignal(t, add_member_response, testSuccess) testSendACL(t, ctx, listener, nil, []Policy{ - NewMemberOfPolicy(map[NodeID]Tree{ - group.ID: nil, + NewMemberOfPolicy(map[NodeID]map[string]Tree{ + group.ID: { + "test_group": nil, + }, }), }, testSuccess) @@ -107,7 +118,15 @@ func TestACLBasic(t *testing.T) { NewACLProxyPolicy([]NodeID{acl_proxy_2.ID}), }, testSuccess) - acl_proxy_3, err := NewNode(ctx, nil, BaseNodeType, 100, []Policy{DefaultACLPolicy}, NewACLExt([]Policy{NewMemberOfPolicy(map[NodeID]Tree{group.ID: nil})})) + acl_proxy_3, err := NewNode(ctx, nil, BaseNodeType, 100, []Policy{DefaultACLPolicy}, + NewACLExt([]Policy{ + NewMemberOfPolicy(map[NodeID]map[string]Tree{ + group.ID: { + "test_group": nil, + }, + }), + }), + ) fatalErr(t, err) testSendACL(t, ctx, listener, nil, []Policy{ diff --git a/context.go b/context.go index 6c63f88..46d0f5a 100644 --- a/context.go +++ b/context.go @@ -1304,6 +1304,16 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) { return nil, err } + err = ctx.RegisterSignal(reflect.TypeOf(AddSubGroupSignal{}), AddSubGroupSignalType) + if err != nil { + return nil, err + } + + err = ctx.RegisterSignal(reflect.TypeOf(RemoveSubGroupSignal{}), RemoveSubGroupSignalType) + if err != nil { + return nil, err + } + err = ctx.RegisterSignal(reflect.TypeOf(ACLTimeoutSignal{}), ACLTimeoutSignalType) if err != nil { return nil, err diff --git a/gql.go b/gql.go index 3a5c8d7..300bc82 100644 --- a/gql.go +++ b/gql.go @@ -1176,25 +1176,71 @@ func NewGQLExtContext() *GQLExtContext { panic(err) } - err = context.RegisterField(context.Interfaces["Node"].List, "Members", GroupExtType, "members", + sub_group_type := graphql.NewObject(graphql.ObjectConfig{ + Name: "SubGroup", + Interfaces: nil, + Fields: graphql.Fields{ + "Name": &graphql.Field{ + Type: graphql.String, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + val, ok := p.Source.(SubGroupGQL) + if ok == false { + return nil, fmt.Errorf("WRONG_TYPE_RETURNED") + } + return val.Name, nil + }, + }, + "Members": &graphql.Field{ + Type: context.Interfaces["Node"].List, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + ctx, err := PrepResolve(p) + if err != nil { + return nil, err + } + + val, ok := p.Source.(SubGroupGQL) + if ok == false { + return nil, fmt.Errorf("WRONG_TYPE_RETURNED") + } + + nodes, err := ResolveNodes(ctx, p, val.Members) + if err != nil { + return nil, err + } + + return nodes, nil + }, + }, + }, + IsTypeOf: func(p graphql.IsTypeOfParams) bool { + return reflect.TypeOf(p.Value) == reflect.TypeOf(SubGroupGQL{}) + }, + Description: "SubGroup within Group", + }) + context.Types = append(context.Types, sub_group_type) + + err = context.RegisterField(sub_group_type, "SubGroups", GroupExtType, "sub_groups", func(p graphql.ResolveParams, ctx *ResolveContext, value reflect.Value)(interface{}, error) { - node_list, ok := value.Interface().([]NodeID) + node_map, ok := value.Interface().(map[string]SubGroup) if ok == false { - return nil, fmt.Errorf("value is %+v, not []NodeID", value.Type()) + return nil, fmt.Errorf("value is %+v, not map[string]SubGroup", value.Type()) } - nodes, err := ResolveNodes(ctx, p, node_list) - if err != nil { - return nil, err + sub_groups := []SubGroupGQL{} + for name, sub_group := range(node_map) { + sub_groups = append(sub_groups, SubGroupGQL{ + name, + sub_group.Members, + }) } - return nodes, nil + return sub_groups, nil }) if err != nil { panic(err) } - err = context.RegisterInterface("Group", "DefaultGroup", []string{"Node"}, []string{"Members"}, map[string]SelfField{}, map[string]ListField{}) + err = context.RegisterInterface("Group", "DefaultGroup", []string{"Node"}, []string{"SubGroups"}, map[string]SelfField{}, map[string]ListField{}) if err != nil { panic(err) } @@ -1242,7 +1288,7 @@ func NewGQLExtContext() *GQLExtContext { panic(err) } - err = context.RegisterNodeType(GQLNodeType, "GQLServer", []string{"Node", "Lockable", "Group"}, []string{"Listen", "Owner", "Requirements", "Members"}) + err = context.RegisterNodeType(GQLNodeType, "GQLServer", []string{"Node", "Lockable", "Group"}, []string{"Listen", "Owner", "Requirements", "SubGroups"}) if err != nil { panic(err) } diff --git a/gql_test.go b/gql_test.go index d02bb71..5916c87 100644 --- a/gql_test.go +++ b/gql_test.go @@ -63,12 +63,14 @@ func TestGQLServer(t *testing.T) { SerializedType(ErrorSignalType): nil, }) - group_policy_2 := NewMemberOfPolicy(map[NodeID]Tree{ + group_policy_2 := NewMemberOfPolicy(map[NodeID]map[string]Tree{ gql_id: { - SerializedType(LinkSignalType): nil, - SerializedType(LockSignalType): nil, - SerializedType(StatusSignalType): nil, - SerializedType(ReadSignalType): nil, + "test_group": { + SerializedType(LinkSignalType): nil, + SerializedType(LockSignalType): nil, + SerializedType(StatusSignalType): nil, + SerializedType(ReadSignalType): nil, + }, }, }) @@ -77,11 +79,13 @@ func TestGQLServer(t *testing.T) { SerializedType(ErrorSignalType): nil, }) - user_policy_2 := NewMemberOfPolicy(map[NodeID]Tree{ + user_policy_2 := NewMemberOfPolicy(map[NodeID]map[string]Tree{ gql_id: { - SerializedType(LinkSignalType): nil, - SerializedType(ReadSignalType): nil, - SerializedType(LockSignalType): nil, + "test_group": { + SerializedType(LinkSignalType): nil, + SerializedType(ReadSignalType): nil, + SerializedType(LockSignalType): nil, + }, }, }) @@ -93,7 +97,7 @@ func TestGQLServer(t *testing.T) { fatalErr(t, err) gql, err := NewNode(ctx, gql_key, GQLNodeType, 10, []Policy{group_policy_2, group_policy_1}, - NewLockableExt([]NodeID{n1.ID}), gql_ext, NewGroupExt([]NodeID{n1.ID, gql_id}), listener_ext) + NewLockableExt([]NodeID{n1.ID}), gql_ext, NewGroupExt(map[string][]NodeID{"test_group": {n1.ID, gql_id}}), listener_ext) fatalErr(t, err) ctx.Log.Logf("test", "GQL: %s", gql.ID) @@ -120,7 +124,7 @@ func TestGQLServer(t *testing.T) { } req_2 := GQLPayload{ - Query: "query Node($id:String) { Node(id:$id) { ID, TypeHash, ... on GQLServer { Members { ID } , Listen, Requirements { ID, TypeHash Owner { ID } } } } }", + Query: "query Node($id:String) { Node(id:$id) { ID, TypeHash, ... on GQLServer { SubGroups { Name, Members { ID } } , Listen, Requirements { ID, TypeHash Owner { ID } } } } }", Variables: map[string]interface{}{ "id": gql.ID.String(), }, diff --git a/group.go b/group.go index 9bb613c..419edb5 100644 --- a/group.go +++ b/group.go @@ -4,38 +4,91 @@ import ( "slices" ) +type AddSubGroupSignal struct { + SignalHeader + Name string `gv:"name"` +} + +func NewAddSubGroupSignal(name string) *AddSubGroupSignal { + return &AddSubGroupSignal{ + NewSignalHeader(Direct), + name, + } +} + +func (signal AddSubGroupSignal) Permission() Tree { + return Tree{ + SerializedType(AddSubGroupSignalType): { + Hash("name", signal.Name): nil, + }, + } +} + +type RemoveSubGroupSignal struct { + SignalHeader + Name string `gv:"name"` +} + +func NewRemoveSubGroupSignal(name string) *RemoveSubGroupSignal { + return &RemoveSubGroupSignal{ + NewSignalHeader(Direct), + name, + } +} + +func (signal RemoveSubGroupSignal) Permission() Tree { + return Tree{ + SerializedType(RemoveSubGroupSignalType): { + Hash("command", signal.Name): nil, + }, + } +} + type AddMemberSignal struct { SignalHeader + SubGroup string `gv:"sub_group"` MemberID NodeID `gv:"member_id"` } +type SubGroupGQL struct { + Name string + Members []NodeID +} + func (signal AddMemberSignal) Permission() Tree { return Tree{ - SerializedType(AddMemberSignalType): nil, + SerializedType(AddMemberSignalType): { + Hash("sub_group", signal.SubGroup): nil, + }, } } -func NewAddMemberSignal(member_id NodeID) *AddMemberSignal { +func NewAddMemberSignal(sub_group string, member_id NodeID) *AddMemberSignal { return &AddMemberSignal{ NewSignalHeader(Direct), + sub_group, member_id, } } type RemoveMemberSignal struct { SignalHeader + SubGroup string `gv:"sub_group"` MemberID NodeID `gv:"member_id"` } func (signal RemoveMemberSignal) Permission() Tree { return Tree{ - SerializedType(RemoveMemberSignalType): nil, + SerializedType(RemoveMemberSignalType): { + Hash("sub_group", signal.SubGroup): nil, + }, } } -func NewRemoveMemberSignal(member_id NodeID) *RemoveMemberSignal { +func NewRemoveMemberSignal(sub_group string, member_id NodeID) *RemoveMemberSignal { return &RemoveMemberSignal{ NewSignalHeader(Direct), + sub_group, member_id, } } @@ -43,18 +96,123 @@ func NewRemoveMemberSignal(member_id NodeID) *RemoveMemberSignal { var DefaultGroupPolicy = NewAllNodesPolicy(Tree{ SerializedType(ReadSignalType): { SerializedType(GroupExtType): { - Hash(FieldNameBase, "members"): nil, + Hash(FieldNameBase, "sub_groups"): nil, }, }, }) +type SubGroup struct { + Members []NodeID + Permissions Tree +} + +type MemberOfPolicy struct { + PolicyHeader + Groups map[NodeID]map[string]Tree +} + +func NewMemberOfPolicy(groups map[NodeID]map[string]Tree) MemberOfPolicy { + return MemberOfPolicy{ + PolicyHeader: NewPolicyHeader(), + Groups: groups, + } +} + +func (policy MemberOfPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult { + sig, ok := signal.(*ReadResultSignal) + if ok == false { + return Deny + } + ctx.Log.Logf("group", "member_of_read_result: %+v", sig.Extensions) + + group_ext_data, ok := sig.Extensions[GroupExtType] + if ok == false { + return Deny + } + + sub_groups_ser, ok := group_ext_data["sub_groups"] + if ok == false { + return Deny + } + + _, sub_groups_if, _, err := DeserializeValue(ctx, sub_groups_ser) + if err != nil { + return Deny + } + + ext_sub_groups, ok := sub_groups_if.Interface().(map[string][]NodeID) + if ok == false { + return Deny + } + + group, exists := policy.Groups[sig.NodeID] + if exists == false { + return Deny + } + + for sub_group_name, permissions := range(group) { + ext_sub_group, exists := ext_sub_groups[sub_group_name] + if exists == true { + for _, member_id := range(ext_sub_group) { + if member_id == current.Principal { + if permissions.Allows(current.Action) == Allow { + return Allow + } + } + } + } + } + + return Deny +} + +// Send a read signal to Group to check if principal_id is a member of it +func (policy MemberOfPolicy) Allows(ctx *Context, principal_id NodeID, action Tree, node *Node) (Messages, RuleResult) { + var messages Messages = nil + for group_id, sub_groups := range(policy.Groups) { + if group_id == node.ID { + ext, err := GetExt[*GroupExt](node, GroupExtType) + if err != nil { + ctx.Log.Logf("group", "MemberOfPolicy with self ID error: %s", err) + } else { + for sub_group_name, sub_group := range(sub_groups) { + ext_sub_group, exists := ext.SubGroups[sub_group_name] + if exists == true { + for _, member := range(ext_sub_group) { + if member == principal_id { + if sub_group.Allows(action) == Allow { + return nil, Allow + } + break + } + } + } + } + } + } else { + // Send the read request to the group so that ContinueAllows can parse the response to check membership + messages = messages.Add(ctx, group_id, node, nil, NewReadSignal(map[ExtType][]string{ + GroupExtType: {"sub_groups"}, + })) + } + } + if len(messages) > 0 { + return messages, Pending + } else { + return nil, Deny + } +} + type GroupExt struct { - Members []NodeID `gv:"members"` + SubGroups map[string][]NodeID `gv:"sub_groups"` } -func NewGroupExt(members []NodeID) *GroupExt { +func NewGroupExt(sub_groups map[string][]NodeID) *GroupExt { + if sub_groups == nil { + sub_groups = map[string][]NodeID{} + } return &GroupExt{ - Members: members, + SubGroups: sub_groups, } } @@ -64,21 +222,57 @@ func (ext *GroupExt) Process(ctx *Context, node *Node, source NodeID, signal Sig switch sig := signal.(type) { case *AddMemberSignal: - if slices.Contains(ext.Members, sig.MemberID) == true { - messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "already_member")) + sub_group, exists := ext.SubGroups[sig.SubGroup] + if exists == false { + messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "not_subgroup")) } else { - ext.Members = append(ext.Members, sig.MemberID) - messages = messages.Add(ctx, source, node, nil, NewSuccessSignal(sig.Id)) - changes = changes.Add("member_added") + if slices.Contains(sub_group, sig.MemberID) == true { + messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "already_member")) + } else { + sub_group = append(sub_group, sig.MemberID) + ext.SubGroups[sig.SubGroup] = sub_group + + messages = messages.Add(ctx, source, node, nil, NewSuccessSignal(sig.Id)) + changes = changes.Add("member_added") + } } + case *RemoveMemberSignal: - idx := slices.Index(ext.Members, sig.MemberID) - if idx == -1 { - messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "not_member")) + sub_group, exists := ext.SubGroups[sig.SubGroup] + if exists == false { + messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "not_subgroup")) } else { - ext.Members = slices.Delete(ext.Members, idx, idx+1) + idx := slices.Index(sub_group, sig.MemberID) + if idx == -1 { + messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "not_member")) + } else { + sub_group = slices.Delete(sub_group, idx, idx+1) + ext.SubGroups[sig.SubGroup] = sub_group + + messages = messages.Add(ctx, source, node, nil, NewSuccessSignal(sig.Id)) + changes = changes.Add("member_removed") + } + } + + case *AddSubGroupSignal: + _, exists := ext.SubGroups[sig.Name] + if exists == true { + messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "already_subgroup")) + } else { + ext.SubGroups[sig.Name] = []NodeID{} + + changes = changes.Add("subgroup_added") + messages = messages.Add(ctx, source, node, nil, NewSuccessSignal(sig.Id)) + } + case *RemoveSubGroupSignal: + _, exists := ext.SubGroups[sig.Name] + if exists == false { + messages = messages.Add(ctx, source, node, nil, NewErrorSignal(sig.Id, "not_subgroup")) + } else { + delete(ext.SubGroups, sig.Name) + + changes = changes.Add("subgroup_removed") messages = messages.Add(ctx, source, node, nil, NewSuccessSignal(sig.Id)) - changes = changes.Add("member_removed") } } diff --git a/group_test.go b/group_test.go index b09f15f..ae9bff0 100644 --- a/group_test.go +++ b/group_test.go @@ -12,18 +12,36 @@ func TestGroupAdd(t *testing.T) { group, err := NewNode(ctx, nil, GroupNodeType, 10, nil, group_listener, NewGroupExt(nil)) fatalErr(t, err) + add_subgroup_signal := NewAddSubGroupSignal("test_group") + messages := Messages{} + messages = messages.Add(ctx, group.ID, group, nil, add_subgroup_signal) + fatalErr(t, ctx.Send(messages)) + + resp_1, err := WaitForResponse(group_listener.Chan, 10*time.Millisecond, add_subgroup_signal.Id) + fatalErr(t, err) + + error_1, is_error := resp_1.(*ErrorSignal) + if is_error { + t.Fatalf("Error returned: %s", error_1.Error) + } + user_id := RandID() - add_member_signal := NewAddMemberSignal(user_id) + add_member_signal := NewAddMemberSignal("test_group", user_id) - messages := Messages{} + messages = Messages{} messages = messages.Add(ctx, group.ID, group, nil, add_member_signal) fatalErr(t, ctx.Send(messages)) - _, err = WaitForResponse(group_listener.Chan, 10*time.Millisecond, add_member_signal.Id) + resp_2, err := WaitForResponse(group_listener.Chan, 10*time.Millisecond, add_member_signal.Id) fatalErr(t, err) + error_2, is_error := resp_2.(*ErrorSignal) + if is_error { + t.Fatalf("Error returned: %s", error_2.Error) + } + read_signal := NewReadSignal(map[ExtType][]string{ - GroupExtType: {"members"}, + GroupExtType: {"sub_groups"}, }) messages = Messages{} @@ -35,26 +53,35 @@ func TestGroupAdd(t *testing.T) { read_response := response.(*ReadResultSignal) - members_serialized := read_response.Extensions[GroupExtType]["members"] + sub_groups_serialized := read_response.Extensions[GroupExtType]["sub_groups"] - _, member_value, remaining, err := DeserializeValue(ctx, members_serialized) + _, sub_groups_value, remaining, err := DeserializeValue(ctx, sub_groups_serialized) if len(remaining.Data) > 0 { - t.Fatalf("Data remaining after deserializing member list: %d", len(remaining.Data)) + t.Fatalf("Data remaining after deserializing subgroups: %d", len(remaining.Data)) } - member_list, ok := member_value.Interface().([]NodeID) + sub_groups, ok := sub_groups_value.Interface().(map[string][]NodeID) if ok != true { - t.Fatalf("member_list wrong type %s", member_value.Type()) + t.Fatalf("sub_groups wrong type %s", sub_groups_value.Type()) + } + + if len(sub_groups) != 1 { + t.Fatalf("sub_groups wrong length %d", len(sub_groups)) + } + + test_subgroup, exists := sub_groups["test_group"] + if exists == false { + t.Fatal("test_group not in subgroups") } - if len(member_list) != 1 { - t.Fatalf("member_list wrong length %d", len(member_list)) + if len(test_subgroup) != 1 { + t.Fatalf("test_group wrong size %d/1", len(test_subgroup)) } - if member_list[0] != user_id { - t.Fatalf("member_list wrong value %s", member_list[0]) + if test_subgroup[0] != user_id { + t.Fatalf("sub_groups wrong value %s", test_subgroup[0]) } ctx.Log.Logf("test", "Read Response: %+v", read_response) diff --git a/lockable.go b/lockable.go index 75efb2d..f06c320 100644 --- a/lockable.go +++ b/lockable.go @@ -327,3 +327,47 @@ func (ext *LockableExt) Process(ctx *Context, node *Node, source NodeID, signal return messages, changes } +type RequirementOfPolicy struct { + PerNodePolicy +} + +func NewRequirementOfPolicy(dep_rules map[NodeID]Tree) RequirementOfPolicy { + return RequirementOfPolicy { + PerNodePolicy: NewPerNodePolicy(dep_rules), + } +} + +func (policy RequirementOfPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult { + sig, ok := signal.(*ReadResultSignal) + if ok == false { + return Deny + } + + ext, ok := sig.Extensions[LockableExtType] + if ok == false { + return Deny + } + + reqs_ser, ok := ext["requirements"] + if ok == false { + return Deny + } + + _, reqs_if, _, err := DeserializeValue(ctx, reqs_ser) + if err != nil { + return Deny + } + + requirements, ok := reqs_if.Interface().(map[NodeID]ReqState) + if ok == false { + return Deny + } + + for req, _ := range(requirements) { + if req == current.Principal { + return policy.NodeRules[sig.NodeID].Allows(current.Action) + } + } + + return Deny +} diff --git a/policy.go b/policy.go index 2fbeb79..33c30f8 100644 --- a/policy.go +++ b/policy.go @@ -40,121 +40,6 @@ func (policy PerNodePolicy) ContinueAllows(ctx *Context, current PendingACL, sig return Deny } -type RequirementOfPolicy struct { - PerNodePolicy -} - -func NewRequirementOfPolicy(dep_rules map[NodeID]Tree) RequirementOfPolicy { - return RequirementOfPolicy { - PerNodePolicy: NewPerNodePolicy(dep_rules), - } -} - -func (policy RequirementOfPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult { - sig, ok := signal.(*ReadResultSignal) - if ok == false { - return Deny - } - - ext, ok := sig.Extensions[LockableExtType] - if ok == false { - return Deny - } - - reqs_ser, ok := ext["requirements"] - if ok == false { - return Deny - } - - _, reqs_if, _, err := DeserializeValue(ctx, reqs_ser) - if err != nil { - return Deny - } - - requirements, ok := reqs_if.Interface().(map[NodeID]ReqState) - if ok == false { - return Deny - } - - for req, _ := range(requirements) { - if req == current.Principal { - return policy.NodeRules[sig.NodeID].Allows(current.Action) - } - } - - return Deny -} - -type MemberOfPolicy struct { - PerNodePolicy -} - -func NewMemberOfPolicy(group_rules map[NodeID]Tree) MemberOfPolicy { - return MemberOfPolicy{ - PerNodePolicy: NewPerNodePolicy(group_rules), - } -} - -func (policy MemberOfPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult { - sig, ok := signal.(*ReadResultSignal) - if ok == false { - return Deny - } - ctx.Log.Logf("group", "member_of_read_result: %+v", sig.Extensions) - - group_ext_data, ok := sig.Extensions[GroupExtType] - if ok == false { - return Deny - } - - members_ser, ok := group_ext_data["members"] - if ok == false { - return Deny - } - - _, members_if, _, err := DeserializeValue(ctx, members_ser) - if err != nil { - return Deny - } - - members, ok := members_if.Interface().([]NodeID) - if ok == false { - return Deny - } - - for _, member := range(members) { - if member == current.Principal { - return policy.NodeRules[sig.NodeID].Allows(current.Action) - } - } - - return Deny -} - -// Send a read signal to Group to check if principal_id is a member of it -func (policy MemberOfPolicy) Allows(ctx *Context, principal_id NodeID, action Tree, node *Node) (Messages, RuleResult) { - msgs := Messages{} - for id, rule := range(policy.NodeRules) { - if id == node.ID { - ext, err := GetExt[*GroupExt](node, GroupExtType) - if err == nil { - for _, member := range(ext.Members) { - if member == principal_id { - if rule.Allows(action) == Allow { - return nil, Allow - } - } - } - } - } else { - msgs = msgs.Add(ctx, id, node, nil, NewReadSignal(map[ExtType][]string{ - GroupExtType: []string{"members"}, - })) - } - } - return msgs, Pending -} - func CopyTree(tree Tree) Tree { if tree == nil { return nil diff --git a/serialize.go b/serialize.go index a70df42..f22002f 100644 --- a/serialize.go +++ b/serialize.go @@ -89,21 +89,23 @@ var ( BaseNodeType = NewNodeType("BASE") GroupNodeType = NewNodeType("GROUP") - StopSignalType = NewSignalType("STOP") - CreateSignalType = NewSignalType("CREATE") - StartSignalType = NewSignalType("START") - StatusSignalType = NewSignalType("STATUS") - LinkSignalType = NewSignalType("LINK") - LockSignalType = NewSignalType("LOCK") - TimeoutSignalType = NewSignalType("TIMEOUT") - ReadSignalType = NewSignalType("READ") - ACLTimeoutSignalType = NewSignalType("ACL_TIMEOUT") - ErrorSignalType = NewSignalType("ERROR") - SuccessSignalType = NewSignalType("SUCCESS") - ReadResultSignalType = NewSignalType("READ_RESULT") - RemoveMemberSignalType = NewSignalType("REMOVE_MEMBER") - AddMemberSignalType = NewSignalType("ADD_MEMBER") - ACLSignalType = NewSignalType("ACL") + StopSignalType = NewSignalType("STOP") + CreateSignalType = NewSignalType("CREATE") + StartSignalType = NewSignalType("START") + StatusSignalType = NewSignalType("STATUS") + LinkSignalType = NewSignalType("LINK") + LockSignalType = NewSignalType("LOCK") + TimeoutSignalType = NewSignalType("TIMEOUT") + ReadSignalType = NewSignalType("READ") + ACLTimeoutSignalType = NewSignalType("ACL_TIMEOUT") + ErrorSignalType = NewSignalType("ERROR") + SuccessSignalType = NewSignalType("SUCCESS") + ReadResultSignalType = NewSignalType("READ_RESULT") + RemoveMemberSignalType = NewSignalType("REMOVE_MEMBER") + AddMemberSignalType = NewSignalType("ADD_MEMBER") + ACLSignalType = NewSignalType("ACL") + AddSubGroupSignalType = NewSignalType("ADD_SUBGROUP") + RemoveSubGroupSignalType = NewSignalType("REMOVE_SUBGROUP") MemberOfPolicyType = NewPolicyType("USER_OF") RequirementOfPolicyType = NewPolicyType("REQUIEMENT_OF") diff --git a/serialize_test.go b/serialize_test.go index 68cd492..1e91138 100644 --- a/serialize_test.go +++ b/serialize_test.go @@ -6,6 +6,11 @@ import ( "fmt" ) +func TestSerializeTest(t *testing.T) { + ctx := logTestContext(t, []string{"test", "serialize"}) + testSerialize(t, ctx, map[string][]NodeID{"test_group": {RandID(), RandID(), RandID()}}) +} + func TestSerializeBasic(t *testing.T) { ctx := logTestContext(t, []string{"test"}) testSerializeComparable[string](t, ctx, "test") @@ -29,6 +34,7 @@ func TestSerializeBasic(t *testing.T) { testSerialize(t, ctx, map[int8]map[*int8]string{}) + var i interface{} = nil testSerialize(t, ctx, i)