2023-07-20 23:19:10 -06:00
|
|
|
package graphvent
|
|
|
|
|
|
|
|
import (
|
2023-09-20 19:14:28 -06:00
|
|
|
"github.com/google/uuid"
|
2023-07-27 23:15:58 -06:00
|
|
|
)
|
|
|
|
|
2023-07-20 23:19:10 -06:00
|
|
|
type Policy interface {
|
2023-08-31 19:50:32 -06:00
|
|
|
Allows(ctx *Context, principal_id NodeID, action Tree, node *Node)(Messages, RuleResult)
|
|
|
|
ContinueAllows(ctx *Context, current PendingACL, signal Signal)RuleResult
|
2023-09-20 19:14:28 -06:00
|
|
|
ID() uuid.UUID
|
|
|
|
}
|
|
|
|
|
|
|
|
type PolicyHeader struct {
|
|
|
|
UUID uuid.UUID `gv:"uuid"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (header PolicyHeader) ID() uuid.UUID {
|
|
|
|
return header.UUID
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
|
2023-09-12 20:30:18 -06:00
|
|
|
func (policy AllNodesPolicy) Allows(ctx *Context, principal_id NodeID, action Tree, node *Node)(Messages, RuleResult) {
|
2023-08-10 23:43:10 -06:00
|
|
|
return nil, policy.Rules.Allows(action)
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
|
2023-09-12 20:30:18 -06:00
|
|
|
func (policy AllNodesPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult {
|
2023-08-10 23:43:10 -06:00
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-09-12 20:30:18 -06:00
|
|
|
func (policy PerNodePolicy) Allows(ctx *Context, principal_id NodeID, action Tree, node *Node)(Messages, RuleResult) {
|
2023-08-10 23:43:10 -06:00
|
|
|
for id, actions := range(policy.NodeRules) {
|
2023-07-27 01:30:32 -06:00
|
|
|
if id != principal_id {
|
|
|
|
continue
|
|
|
|
}
|
2023-08-08 14:00:17 -06:00
|
|
|
return nil, actions.Allows(action)
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
2023-08-10 23:43:10 -06:00
|
|
|
return nil, Deny
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
|
2023-09-12 20:30:18 -06:00
|
|
|
func (policy PerNodePolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult {
|
2023-08-10 23:43:10 -06:00
|
|
|
return Deny
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
|
2023-08-11 16:00:36 -06:00
|
|
|
type RequirementOfPolicy struct {
|
|
|
|
PerNodePolicy
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRequirementOfPolicy(dep_rules map[NodeID]Tree) RequirementOfPolicy {
|
|
|
|
return RequirementOfPolicy {
|
|
|
|
PerNodePolicy: NewPerNodePolicy(dep_rules),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-12 20:30:18 -06:00
|
|
|
func (policy RequirementOfPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult {
|
2023-08-11 16:00:36 -06:00
|
|
|
sig, ok := signal.(*ReadResultSignal)
|
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
|
|
|
ext, ok := sig.Extensions[LockableExtType]
|
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-08-31 19:50:32 -06:00
|
|
|
reqs_ser, ok := ext["requirements"]
|
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-09-05 00:08:09 -06:00
|
|
|
_, reqs_if, _, err := DeserializeValue(ctx, reqs_ser)
|
2023-08-31 19:50:32 -06:00
|
|
|
if err != nil {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-09-05 00:08:09 -06:00
|
|
|
requirements, ok := reqs_if.Interface().(map[NodeID]ReqState)
|
2023-08-11 16:00:36 -06:00
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
|
|
|
for req, _ := range(requirements) {
|
|
|
|
if req == current.Principal {
|
|
|
|
return policy.NodeRules[sig.NodeID].Allows(current.Action)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-08-10 23:43:10 -06:00
|
|
|
type MemberOfPolicy struct {
|
2023-08-07 20:26:02 -06:00
|
|
|
PerNodePolicy
|
|
|
|
}
|
|
|
|
|
2023-08-11 16:00:36 -06:00
|
|
|
func NewMemberOfPolicy(group_rules map[NodeID]Tree) MemberOfPolicy {
|
2023-08-10 23:43:10 -06:00
|
|
|
return MemberOfPolicy{
|
|
|
|
PerNodePolicy: NewPerNodePolicy(group_rules),
|
|
|
|
}
|
2023-08-07 20:26:02 -06:00
|
|
|
}
|
|
|
|
|
2023-09-12 20:30:18 -06:00
|
|
|
func (policy MemberOfPolicy) ContinueAllows(ctx *Context, current PendingACL, signal Signal) RuleResult {
|
2023-08-10 23:43:10 -06:00
|
|
|
sig, ok := signal.(*ReadResultSignal)
|
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
2023-10-13 00:32:24 -06:00
|
|
|
ctx.Log.Logf("group", "member_of_read_result: %+v", sig.Extensions)
|
2023-08-10 23:43:10 -06:00
|
|
|
|
|
|
|
group_ext_data, ok := sig.Extensions[GroupExtType]
|
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-08-31 19:50:32 -06:00
|
|
|
members_ser, ok := group_ext_data["members"]
|
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-09-05 00:08:09 -06:00
|
|
|
_, members_if, _, err := DeserializeValue(ctx, members_ser)
|
2023-08-31 19:50:32 -06:00
|
|
|
if err != nil {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-10-13 00:32:24 -06:00
|
|
|
members, ok := members_if.Interface().([]NodeID)
|
2023-08-10 23:43:10 -06:00
|
|
|
if ok == false {
|
|
|
|
return Deny
|
|
|
|
}
|
|
|
|
|
2023-10-13 00:32:24 -06:00
|
|
|
for _, member := range(members) {
|
2023-08-10 23:43:10 -06:00
|
|
|
if member == current.Principal {
|
|
|
|
return policy.NodeRules[sig.NodeID].Allows(current.Action)
|
|
|
|
}
|
2023-08-07 20:26:02 -06:00
|
|
|
}
|
2023-08-10 23:43:10 -06:00
|
|
|
|
|
|
|
return Deny
|
2023-08-07 20:26:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Send a read signal to Group to check if principal_id is a member of it
|
2023-09-12 20:30:18 -06:00
|
|
|
func (policy MemberOfPolicy) Allows(ctx *Context, principal_id NodeID, action Tree, node *Node) (Messages, RuleResult) {
|
2023-08-10 23:43:10 -06:00
|
|
|
msgs := Messages{}
|
|
|
|
for id, rule := range(policy.NodeRules) {
|
|
|
|
if id == node.ID {
|
2023-08-31 19:50:32 -06:00
|
|
|
ext, err := GetExt[*GroupExt](node, GroupExtType)
|
2023-08-10 23:43:10 -06:00
|
|
|
if err == nil {
|
2023-10-03 21:18:06 -06:00
|
|
|
for _, member := range(ext.Members) {
|
2023-08-10 23:43:10 -06:00
|
|
|
if member == principal_id {
|
|
|
|
if rule.Allows(action) == Allow {
|
|
|
|
return nil, Allow
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2023-10-14 15:05:23 -06:00
|
|
|
msgs = msgs.Add(ctx, id, node, nil, NewReadSignal(map[ExtType][]string{
|
2023-08-10 23:43:10 -06:00
|
|
|
GroupExtType: []string{"members"},
|
2023-10-14 15:05:23 -06:00
|
|
|
}))
|
2023-08-10 23:43:10 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return msgs, Pending
|
2023-08-07 20:26:02 -06:00
|
|
|
}
|
|
|
|
|
2023-08-10 23:43:10 -06:00
|
|
|
func CopyTree(tree Tree) Tree {
|
|
|
|
if tree == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2023-07-26 15:08:14 -06:00
|
|
|
|
2023-08-10 23:43:10 -06:00
|
|
|
ret := Tree{}
|
|
|
|
for name, sub := range(tree) {
|
|
|
|
ret[name] = CopyTree(sub)
|
2023-07-26 15:08:14 -06:00
|
|
|
}
|
2023-08-10 23:43:10 -06:00
|
|
|
|
|
|
|
return ret
|
2023-07-26 15:08:14 -06:00
|
|
|
}
|
|
|
|
|
2023-08-10 23:43:10 -06:00
|
|
|
func MergeTrees(first Tree, second Tree) Tree {
|
|
|
|
if first == nil || second == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := CopyTree(first)
|
|
|
|
for name, sub := range(second) {
|
|
|
|
current, exists := ret[name]
|
|
|
|
if exists == true {
|
|
|
|
ret[name] = MergeTrees(current, sub)
|
|
|
|
} else {
|
|
|
|
ret[name] = CopyTree(sub)
|
|
|
|
}
|
2023-07-28 00:32:43 -06:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2023-09-05 00:08:09 -06:00
|
|
|
type Tree map[SerializedType]Tree
|
2023-08-10 23:43:10 -06:00
|
|
|
|
|
|
|
func (rule Tree) Allows(action Tree) RuleResult {
|
|
|
|
// If the current rule is nil, it's a wildcard and any action being processed is allowed
|
|
|
|
if rule == nil {
|
|
|
|
return Allow
|
2023-08-31 19:50:32 -06:00
|
|
|
// If the rule isn't "allow all" but the action is "request all", deny
|
2023-08-10 23:43:10 -06:00
|
|
|
} else if action == nil {
|
|
|
|
return Deny
|
2023-08-31 19:50:32 -06:00
|
|
|
// If the current action has no children, it's allowed
|
2023-08-10 23:43:10 -06:00
|
|
|
} else if len(action) == 0 {
|
|
|
|
return Allow
|
2023-08-31 19:50:32 -06:00
|
|
|
// If the current rule has no children but the action goes further, it's not allowed
|
2023-08-10 23:43:10 -06:00
|
|
|
} else if len(rule) == 0 {
|
|
|
|
return Deny
|
2023-08-31 19:50:32 -06:00
|
|
|
// If the current rule and action have children, all the children of action must be allowed by rule
|
2023-08-10 23:43:10 -06:00
|
|
|
} else {
|
|
|
|
for sub, subtree := range(action) {
|
|
|
|
subrule, exists := rule[sub]
|
|
|
|
if exists == false {
|
|
|
|
return Deny
|
|
|
|
} else if subrule.Allows(subtree) == Deny {
|
|
|
|
return Deny
|
2023-07-28 10:04:31 -06:00
|
|
|
}
|
|
|
|
}
|
2023-08-10 23:43:10 -06:00
|
|
|
return Allow
|
2023-07-28 10:04:31 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-20 19:14:28 -06:00
|
|
|
func NewPolicyHeader() PolicyHeader {
|
|
|
|
return PolicyHeader{
|
|
|
|
UUID: uuid.New(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-11 16:00:36 -06:00
|
|
|
func NewPerNodePolicy(node_actions map[NodeID]Tree) PerNodePolicy {
|
2023-07-26 13:28:03 -06:00
|
|
|
if node_actions == nil {
|
2023-08-11 16:00:36 -06:00
|
|
|
node_actions = map[NodeID]Tree{}
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
|
|
|
|
2023-07-26 15:08:14 -06:00
|
|
|
return PerNodePolicy{
|
2023-09-20 19:14:28 -06:00
|
|
|
PolicyHeader: NewPolicyHeader(),
|
2023-08-10 23:43:10 -06:00
|
|
|
NodeRules: node_actions,
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type PerNodePolicy struct {
|
2023-09-20 19:14:28 -06:00
|
|
|
PolicyHeader
|
|
|
|
NodeRules map[NodeID]Tree `gv:"node_rules"`
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
|
|
|
|
2023-08-10 23:43:10 -06:00
|
|
|
func NewAllNodesPolicy(rules Tree) AllNodesPolicy {
|
2023-07-27 00:30:24 -06:00
|
|
|
return AllNodesPolicy{
|
2023-09-20 19:14:28 -06:00
|
|
|
PolicyHeader: NewPolicyHeader(),
|
2023-08-10 23:43:10 -06:00
|
|
|
Rules: rules,
|
2023-07-27 00:30:24 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type AllNodesPolicy struct {
|
2023-09-20 19:14:28 -06:00
|
|
|
PolicyHeader
|
|
|
|
Rules Tree `gv:"rules"`
|
2023-07-27 00:30:24 -06:00
|
|
|
}
|
|
|
|
|
2023-08-10 23:43:10 -06:00
|
|
|
var DefaultPolicy = NewAllNodesPolicy(Tree{
|
2023-10-01 20:45:44 -06:00
|
|
|
ResponseType: nil,
|
2023-09-27 18:28:56 -06:00
|
|
|
StatusType: nil,
|
2023-08-10 23:43:10 -06:00
|
|
|
})
|