Moved groups to use SubGroups instead so one node can support many sub_groups(admin, ref, user, etc.) to reduce signals sent

gql_cataclysm
noah metz 2023-10-15 15:14:33 -06:00
parent 0159d0dd5a
commit 4b7bc93914
10 changed files with 425 additions and 188 deletions

@ -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{

@ -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

@ -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)
}

@ -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(),
},

@ -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")
}
}

@ -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)

@ -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
}

@ -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

@ -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")

@ -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)