2023-07-20 23:19:10 -06:00
|
|
|
package graphvent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2023-07-25 00:50:26 -06:00
|
|
|
"fmt"
|
2023-07-20 23:19:10 -06:00
|
|
|
)
|
|
|
|
|
2023-07-27 23:15:58 -06:00
|
|
|
type PolicyType string
|
|
|
|
func (policy PolicyType) Hash() uint64 {
|
|
|
|
hash := sha512.Sum512([]byte(fmt.Sprintf("POLICY: %s", string(policy))))
|
|
|
|
return binary.BigEndian.Uint64(hash[(len(hash)-9):(len(hash)-1)])
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
RequirementOfPolicyType = PolicyType("REQUIREMENT_OF")
|
|
|
|
PerNodePolicyType = PolicyType("PER_NODE")
|
|
|
|
AllNodesPolicyType = PolicyType("ALL_NODES")
|
|
|
|
)
|
|
|
|
|
2023-07-20 23:19:10 -06:00
|
|
|
type Policy interface {
|
2023-07-26 15:08:14 -06:00
|
|
|
Serializable[PolicyType]
|
2023-07-27 18:08:43 -06:00
|
|
|
Allows(principal_id NodeID, action SignalType, node *Node) error
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
//TODO: Update with change from principal *Node to principal_id so sane policies can still be made
|
2023-07-27 18:08:43 -06:00
|
|
|
func (policy *AllNodesPolicy) Allows(principal_id NodeID, action SignalType, node *Node) error {
|
2023-07-27 01:30:32 -06:00
|
|
|
return policy.Actions.Allows(action)
|
|
|
|
}
|
|
|
|
|
2023-07-27 18:08:43 -06:00
|
|
|
func (policy *PerNodePolicy) Allows(principal_id NodeID, action SignalType, node *Node) error {
|
2023-07-27 01:30:32 -06:00
|
|
|
for id, actions := range(policy.NodeActions) {
|
|
|
|
if id != principal_id {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, a := range(actions) {
|
|
|
|
if a == action {
|
2023-07-27 09:32:33 -06:00
|
|
|
return nil
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-07-27 09:32:33 -06:00
|
|
|
return fmt.Errorf("%s is not in per node policy of %s", principal_id, node.ID)
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 18:08:43 -06:00
|
|
|
func (policy *RequirementOfPolicy) Allows(principal_id NodeID, action SignalType, node *Node) error {
|
2023-07-27 09:32:33 -06:00
|
|
|
lockable_ext, err := GetExt[*LockableExt](node)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-07-27 18:08:43 -06:00
|
|
|
for id, _ := range(lockable_ext.Requirements) {
|
2023-07-27 09:32:33 -06:00
|
|
|
if id == principal_id {
|
|
|
|
return policy.Actions.Allows(action)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("%s is not a requirement of %s", principal_id, node.ID)
|
2023-07-27 01:30:32 -06:00
|
|
|
}
|
|
|
|
|
2023-07-26 15:08:14 -06:00
|
|
|
type RequirementOfPolicy struct {
|
2023-07-27 09:32:33 -06:00
|
|
|
AllNodesPolicy
|
2023-07-26 15:08:14 -06:00
|
|
|
}
|
|
|
|
func (policy *RequirementOfPolicy) Type() PolicyType {
|
|
|
|
return RequirementOfPolicyType
|
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
func NewRequirementOfPolicy(actions Actions) RequirementOfPolicy {
|
2023-07-26 15:08:14 -06:00
|
|
|
return RequirementOfPolicy{
|
2023-07-27 09:32:33 -06:00
|
|
|
AllNodesPolicy: NewAllNodesPolicy(actions),
|
2023-07-26 15:08:14 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-27 18:08:43 -06:00
|
|
|
type Actions []SignalType
|
2023-07-26 15:40:33 -06:00
|
|
|
|
2023-07-27 18:08:43 -06:00
|
|
|
func (actions Actions) Allows(action SignalType) error {
|
2023-07-26 15:40:33 -06:00
|
|
|
for _, a := range(actions) {
|
|
|
|
if a == action {
|
2023-07-27 09:32:33 -06:00
|
|
|
return nil
|
2023-07-26 15:40:33 -06:00
|
|
|
}
|
|
|
|
}
|
2023-07-27 09:32:33 -06:00
|
|
|
return fmt.Errorf("%s not in allows list", action)
|
2023-07-26 15:40:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type NodeActions map[NodeID]Actions
|
2023-07-26 13:28:03 -06:00
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
type AllNodesPolicyJSON struct {
|
|
|
|
Actions Actions `json:"actions"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func AllNodesPolicyLoad(init_fn func(Actions)(Policy, error)) func(*Context, []byte)(Policy, error) {
|
|
|
|
return func(ctx *Context, data []byte)(Policy, error){
|
|
|
|
var j AllNodesPolicyJSON
|
|
|
|
err := json.Unmarshal(data, &j)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return init_fn(j.Actions)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-26 15:08:14 -06:00
|
|
|
func PerNodePolicyLoad(init_fn func(NodeActions)(Policy, error)) func(*Context, []byte)(Policy, error) {
|
|
|
|
return func(ctx *Context, data []byte)(Policy, error){
|
2023-07-27 18:08:43 -06:00
|
|
|
var policy PerNodePolicy
|
|
|
|
err := json.Unmarshal(data, &policy)
|
2023-07-26 13:28:03 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-07-27 18:08:43 -06:00
|
|
|
return init_fn(policy.NodeActions)
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
2023-07-26 15:08:14 -06:00
|
|
|
}
|
2023-07-26 13:28:03 -06:00
|
|
|
|
2023-07-26 15:08:14 -06:00
|
|
|
func NewPerNodePolicy(node_actions NodeActions) PerNodePolicy {
|
2023-07-26 13:28:03 -06:00
|
|
|
if node_actions == nil {
|
2023-07-26 15:40:33 -06:00
|
|
|
node_actions = NodeActions{}
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
|
|
|
|
2023-07-26 15:08:14 -06:00
|
|
|
return PerNodePolicy{
|
2023-07-26 13:28:03 -06:00
|
|
|
NodeActions: node_actions,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type PerNodePolicy struct {
|
2023-07-27 18:08:43 -06:00
|
|
|
NodeActions NodeActions `json:"node_actions"`
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
|
|
|
|
2023-07-26 15:08:14 -06:00
|
|
|
func (policy *PerNodePolicy) Type() PolicyType {
|
2023-07-26 13:28:03 -06:00
|
|
|
return PerNodePolicyType
|
|
|
|
}
|
|
|
|
|
2023-07-26 15:08:14 -06:00
|
|
|
func (policy *PerNodePolicy) Serialize() ([]byte, error) {
|
2023-07-27 18:08:43 -06:00
|
|
|
return json.MarshalIndent(policy, "", " ")
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 00:30:24 -06:00
|
|
|
func NewAllNodesPolicy(actions Actions) AllNodesPolicy {
|
|
|
|
if actions == nil {
|
|
|
|
actions = Actions{}
|
|
|
|
}
|
|
|
|
|
|
|
|
return AllNodesPolicy{
|
|
|
|
Actions: actions,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type AllNodesPolicy struct {
|
2023-07-27 09:32:33 -06:00
|
|
|
Actions Actions
|
2023-07-27 00:30:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (policy *AllNodesPolicy) Type() PolicyType {
|
|
|
|
return AllNodesPolicyType
|
|
|
|
}
|
|
|
|
|
|
|
|
func (policy *AllNodesPolicy) Serialize() ([]byte, error) {
|
|
|
|
return json.MarshalIndent(policy, "", " ")
|
|
|
|
}
|
|
|
|
|
2023-07-25 21:43:15 -06:00
|
|
|
// Extension to allow a node to hold ACL policies
|
2023-07-27 09:32:33 -06:00
|
|
|
type ACLExt struct {
|
2023-07-25 21:43:15 -06:00
|
|
|
Policies map[PolicyType]Policy
|
2023-07-20 23:19:10 -06:00
|
|
|
}
|
|
|
|
|
2023-07-26 00:18:11 -06:00
|
|
|
|
2023-07-26 13:28:03 -06:00
|
|
|
func NodeList(nodes ...*Node) NodeMap {
|
|
|
|
m := NodeMap{}
|
|
|
|
for _, node := range(nodes) {
|
|
|
|
m[node.ID] = node
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2023-07-25 21:43:15 -06:00
|
|
|
type PolicyLoadFunc func(*Context, []byte) (Policy, error)
|
|
|
|
type PolicyInfo struct {
|
|
|
|
Load PolicyLoadFunc
|
2023-07-20 23:19:10 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
type ACLExtContext struct {
|
2023-07-25 21:43:15 -06:00
|
|
|
Types map[PolicyType]PolicyInfo
|
2023-07-20 23:19:10 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
func NewACLExtContext() *ACLExtContext {
|
|
|
|
return &ACLExtContext{
|
2023-07-26 13:28:03 -06:00
|
|
|
Types: map[PolicyType]PolicyInfo{
|
2023-07-27 09:32:33 -06:00
|
|
|
AllNodesPolicyType: PolicyInfo{
|
|
|
|
Load: AllNodesPolicyLoad(func(actions Actions)(Policy, error){
|
|
|
|
policy := NewAllNodesPolicy(actions)
|
|
|
|
return &policy, nil
|
|
|
|
}),
|
|
|
|
},
|
2023-07-26 13:28:03 -06:00
|
|
|
PerNodePolicyType: PolicyInfo{
|
2023-07-26 15:08:14 -06:00
|
|
|
Load: PerNodePolicyLoad(func(nodes NodeActions)(Policy,error){
|
|
|
|
policy := NewPerNodePolicy(nodes)
|
|
|
|
return &policy, nil
|
|
|
|
}),
|
2023-07-26 13:28:03 -06:00
|
|
|
},
|
2023-07-26 15:08:14 -06:00
|
|
|
RequirementOfPolicyType: PolicyInfo{
|
2023-07-27 09:32:33 -06:00
|
|
|
Load: AllNodesPolicyLoad(func(actions Actions)(Policy, error){
|
|
|
|
policy := NewRequirementOfPolicy(actions)
|
2023-07-26 15:08:14 -06:00
|
|
|
return &policy, nil
|
|
|
|
}),
|
2023-07-26 13:28:03 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2023-07-26 00:18:11 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
func (ext *ACLExt) Serialize() ([]byte, error) {
|
2023-07-25 21:43:15 -06:00
|
|
|
policies := map[string][]byte{}
|
|
|
|
for name, policy := range(ext.Policies) {
|
|
|
|
ser, err := policy.Serialize()
|
2023-07-20 23:19:10 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-07-25 21:43:15 -06:00
|
|
|
policies[string(name)] = ser
|
2023-07-20 23:19:10 -06:00
|
|
|
}
|
|
|
|
|
2023-07-25 21:43:15 -06:00
|
|
|
return json.MarshalIndent(&struct{
|
|
|
|
Policies map[string][]byte `json:"policies"`
|
|
|
|
}{
|
|
|
|
Policies: policies,
|
|
|
|
}, "", " ")
|
2023-07-21 13:55:27 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 15:27:14 -06:00
|
|
|
func (ext *ACLExt) Process(ctx *Context, princ_id NodeID, node *Node, signal Signal) {
|
2023-07-21 13:55:27 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 11:33:11 -06:00
|
|
|
func NewACLExt(policies ...Policy) *ACLExt {
|
|
|
|
policy_map := map[PolicyType]Policy{}
|
|
|
|
for _, policy := range(policies) {
|
|
|
|
_, exists := policy_map[policy.Type()]
|
|
|
|
if exists == true {
|
|
|
|
panic("Cannot add same policy type twice")
|
2023-07-27 00:30:24 -06:00
|
|
|
}
|
2023-07-27 11:33:11 -06:00
|
|
|
|
|
|
|
policy_map[policy.Type()] = policy
|
2023-07-27 00:30:24 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
return &ACLExt{
|
2023-07-27 11:33:11 -06:00
|
|
|
Policies: policy_map,
|
2023-07-26 13:28:03 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
func LoadACLExt(ctx *Context, data []byte) (Extension, error) {
|
2023-07-25 21:43:15 -06:00
|
|
|
var j struct {
|
|
|
|
Policies map[string][]byte `json:"policies"`
|
2023-07-24 22:52:15 -06:00
|
|
|
}
|
2023-07-25 21:43:15 -06:00
|
|
|
err := json.Unmarshal(data, &j)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-07-21 13:55:27 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 11:33:11 -06:00
|
|
|
policies := make([]Policy, len(j.Policies))
|
|
|
|
i := 0
|
2023-07-27 16:06:56 -06:00
|
|
|
acl_ctx, err := GetCtx[*ACLExt, *ACLExtContext](ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-07-25 21:43:15 -06:00
|
|
|
for name, ser := range(j.Policies) {
|
|
|
|
policy_def, exists := acl_ctx.Types[PolicyType(name)]
|
|
|
|
if exists == false {
|
|
|
|
return nil, fmt.Errorf("%s is not a known policy type", name)
|
2023-07-24 22:52:15 -06:00
|
|
|
}
|
2023-07-25 21:43:15 -06:00
|
|
|
policy, err := policy_def.Load(ctx, ser)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2023-07-24 22:52:15 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 11:33:11 -06:00
|
|
|
policies[i] = policy
|
|
|
|
i++
|
2023-07-24 22:52:15 -06:00
|
|
|
}
|
2023-07-21 13:55:27 -06:00
|
|
|
|
2023-07-27 11:33:11 -06:00
|
|
|
return NewACLExt(policies...), nil
|
2023-07-24 01:12:30 -06:00
|
|
|
}
|
|
|
|
|
2023-07-27 09:32:33 -06:00
|
|
|
func (ext *ACLExt) Type() ExtType {
|
|
|
|
return ACLExtType
|
2023-07-24 01:12:30 -06:00
|
|
|
}
|
|
|
|
|
2023-07-25 21:43:15 -06:00
|
|
|
// Check if the extension allows the principal to perform action on node
|
2023-07-27 18:08:43 -06:00
|
|
|
func (ext *ACLExt) Allows(ctx *Context, principal_id NodeID, action SignalType, node *Node) error {
|
2023-07-27 15:27:14 -06:00
|
|
|
ctx.Log.Logf("policy", "POLICY_EXT_ALLOWED: %+v", ext)
|
2023-07-27 09:32:33 -06:00
|
|
|
errs := []error{}
|
2023-07-25 21:43:15 -06:00
|
|
|
for _, policy := range(ext.Policies) {
|
2023-07-27 15:27:14 -06:00
|
|
|
err := policy.Allows(principal_id, action, node)
|
2023-07-27 09:32:33 -06:00
|
|
|
if err == nil {
|
|
|
|
return nil
|
2023-07-24 01:12:30 -06:00
|
|
|
}
|
2023-07-27 09:32:33 -06:00
|
|
|
errs = append(errs, err)
|
2023-07-24 01:12:30 -06:00
|
|
|
}
|
2023-07-27 09:32:33 -06:00
|
|
|
return fmt.Errorf("POLICY_CHECK_ERRORS: %s %s.%s - %+v", principal_id, node.ID, action, errs)
|
2023-07-24 01:12:30 -06:00
|
|
|
}
|