361 lines
8.8 KiB
Go
361 lines
8.8 KiB
Go
package graphvent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// A policy represents a set of rules attached to a Node that allow principals to perform actions on it
|
|
type Policy interface {
|
|
Node
|
|
// Returns true if the principal is allowed to perform the action on the resource
|
|
Allows(context *StateContext, node Node, resource string, action string, principal Node) bool
|
|
}
|
|
|
|
type NodeActions map[string][]string
|
|
func (actions NodeActions) Allows(resource string, action string) bool {
|
|
for _, a := range(actions[""]) {
|
|
if a == action || a == "*" {
|
|
return true
|
|
}
|
|
}
|
|
|
|
resource_actions, exists := actions[resource]
|
|
if exists == true {
|
|
for _, a := range(resource_actions) {
|
|
if a == action || a == "*" {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func NewNodeActions(resource_actions NodeActions, wildcard_actions []string) NodeActions {
|
|
if resource_actions == nil {
|
|
resource_actions = NodeActions{}
|
|
}
|
|
// Wildcard actions, all actions in "" will be allowed on all resources
|
|
if wildcard_actions == nil {
|
|
wildcard_actions = []string{}
|
|
}
|
|
resource_actions[""] = wildcard_actions
|
|
return resource_actions
|
|
}
|
|
|
|
type PerNodePolicy struct {
|
|
SimpleNode
|
|
Actions map[NodeID]NodeActions
|
|
}
|
|
|
|
type PerNodePolicyJSON struct {
|
|
SimpleNodeJSON
|
|
Actions map[string]map[string][]string `json:"actions"`
|
|
}
|
|
|
|
func (policy *PerNodePolicy) Type() NodeType {
|
|
return NodeType("per_node_policy")
|
|
}
|
|
|
|
func (policy *PerNodePolicy) Serialize() ([]byte, error) {
|
|
actions := map[string]map[string][]string{}
|
|
for principal, resource_actions := range(policy.Actions) {
|
|
actions[principal.String()] = resource_actions
|
|
}
|
|
|
|
return json.MarshalIndent(&PerNodePolicyJSON{
|
|
SimpleNodeJSON: NewSimpleNodeJSON(&policy.SimpleNode),
|
|
Actions: actions,
|
|
}, "", " ")
|
|
}
|
|
|
|
func NewPerNodePolicy(id NodeID, actions map[NodeID]NodeActions) PerNodePolicy {
|
|
if actions == nil {
|
|
actions = map[NodeID]NodeActions{}
|
|
}
|
|
|
|
return PerNodePolicy{
|
|
SimpleNode: NewSimpleNode(id),
|
|
Actions: actions,
|
|
}
|
|
}
|
|
|
|
var LoadPerNodePolicy = LoadJSONNode(func(id NodeID, j PerNodePolicyJSON) (Node, error) {
|
|
actions := map[NodeID]NodeActions{}
|
|
for principal_str, node_actions := range(j.Actions) {
|
|
principal_id, err := ParseID(principal_str)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
actions[principal_id] = node_actions
|
|
}
|
|
|
|
policy := NewPerNodePolicy(id, actions)
|
|
return &policy, nil
|
|
}, func(ctx *Context, node Node, j PerNodePolicyJSON, nodes NodeMap) error {
|
|
return RestoreSimpleNode(ctx, node.NodeHandle(), j.SimpleNodeJSON, nodes)
|
|
})
|
|
|
|
func (policy *PerNodePolicy) Allows(context *StateContext, node Node, resource string, action string, principal Node) bool {
|
|
node_actions, exists := policy.Actions[principal.ID()]
|
|
if exists == false {
|
|
return false
|
|
}
|
|
|
|
if node_actions.Allows(resource, action) == true {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
type SimplePolicy struct {
|
|
SimpleNode
|
|
Actions NodeActions
|
|
}
|
|
|
|
type SimplePolicyJSON struct {
|
|
SimpleNodeJSON
|
|
Actions map[string][]string `json:"actions"`
|
|
}
|
|
|
|
func (policy *SimplePolicy) Type() NodeType {
|
|
return NodeType("simple_policy")
|
|
}
|
|
|
|
func NewSimplePolicyJSON(policy *SimplePolicy) SimplePolicyJSON {
|
|
return SimplePolicyJSON{
|
|
SimpleNodeJSON: NewSimpleNodeJSON(&policy.SimpleNode),
|
|
Actions: policy.Actions,
|
|
}
|
|
}
|
|
|
|
func (policy *SimplePolicy) Serialize() ([]byte, error) {
|
|
j := NewSimplePolicyJSON(policy)
|
|
return json.MarshalIndent(&j, "", " ")
|
|
}
|
|
|
|
func NewSimplePolicy(id NodeID, actions NodeActions) SimplePolicy {
|
|
if actions == nil {
|
|
actions = NodeActions{}
|
|
}
|
|
|
|
return SimplePolicy{
|
|
SimpleNode: NewSimpleNode(id),
|
|
Actions: actions,
|
|
}
|
|
}
|
|
|
|
var LoadSimplePolicy = LoadJSONNode(func(id NodeID, j SimplePolicyJSON) (Node, error) {
|
|
policy := NewSimplePolicy(id, j.Actions)
|
|
return &policy, nil
|
|
}, func(ctx *Context, node Node, j SimplePolicyJSON, nodes NodeMap) error {
|
|
return RestoreSimpleNode(ctx, node.NodeHandle(), j.SimpleNodeJSON, nodes)
|
|
})
|
|
|
|
func (policy *SimplePolicy) Allows(context *StateContext, node Node, resource string, action string, principal Node) bool {
|
|
return policy.Actions.Allows(resource, action)
|
|
}
|
|
|
|
|
|
type DependencyPolicy struct {
|
|
SimplePolicy
|
|
}
|
|
|
|
|
|
func (policy *DependencyPolicy) Type() NodeType {
|
|
return NodeType("dependency_policy")
|
|
}
|
|
|
|
func NewDependencyPolicy(id NodeID, actions NodeActions) DependencyPolicy {
|
|
return DependencyPolicy{
|
|
SimplePolicy: NewSimplePolicy(id, actions),
|
|
}
|
|
}
|
|
|
|
func (policy *DependencyPolicy) Allows(context *StateContext, node Node, resource string, action string, principal Node) bool {
|
|
lockable, ok := node.(LockableNode)
|
|
if ok == false {
|
|
return false
|
|
}
|
|
|
|
for _, dep := range(lockable.LockableHandle().Dependencies) {
|
|
if dep.ID() == principal.ID() {
|
|
return policy.Actions.Allows(resource, action)
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
type RequirementPolicy struct {
|
|
SimplePolicy
|
|
}
|
|
|
|
|
|
func (policy *RequirementPolicy) Type() NodeType {
|
|
return NodeType("dependency_policy")
|
|
}
|
|
|
|
func NewRequirementPolicy(id NodeID, actions NodeActions) RequirementPolicy {
|
|
return RequirementPolicy{
|
|
SimplePolicy: NewSimplePolicy(id, actions),
|
|
}
|
|
}
|
|
|
|
func (policy *RequirementPolicy) Allows(context *StateContext, node Node, resource string, action string, principal Node) bool {
|
|
lockable_node, ok := node.(LockableNode)
|
|
if ok == false {
|
|
return false
|
|
}
|
|
lockable := lockable_node.LockableHandle()
|
|
|
|
for _, req := range(lockable.Requirements) {
|
|
if req.ID() == principal.ID() {
|
|
return policy.Actions.Allows(resource, action)
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
type ParentPolicy struct {
|
|
SimplePolicy
|
|
}
|
|
|
|
func (policy *ParentPolicy) Type() NodeType {
|
|
return NodeType("parent_policy")
|
|
}
|
|
|
|
func NewParentPolicy(id NodeID, actions NodeActions) ParentPolicy {
|
|
return ParentPolicy{
|
|
SimplePolicy: NewSimplePolicy(id, actions),
|
|
}
|
|
}
|
|
|
|
func (policy *ParentPolicy) Allows(context *StateContext, node Node, resource string, action string, principal Node) bool {
|
|
thread_node, ok := node.(ThreadNode)
|
|
if ok == false {
|
|
return false
|
|
}
|
|
thread := thread_node.ThreadHandle()
|
|
|
|
if thread.Owner != nil {
|
|
if thread.Owner.ID() == principal.ID() {
|
|
return policy.Actions.Allows(resource, action)
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
type ChildrenPolicy struct {
|
|
SimplePolicy
|
|
}
|
|
|
|
|
|
func (policy *ChildrenPolicy) Type() NodeType {
|
|
return NodeType("children_policy")
|
|
}
|
|
|
|
func NewChildrenPolicy(id NodeID, actions NodeActions) ChildrenPolicy {
|
|
return ChildrenPolicy{
|
|
SimplePolicy: NewSimplePolicy(id, actions),
|
|
}
|
|
}
|
|
|
|
func (policy *ChildrenPolicy) Allows(context *StateContext, node Node, resource string, action string, principal Node) bool {
|
|
thread_node, ok := node.(ThreadNode)
|
|
if ok == false {
|
|
return false
|
|
}
|
|
thread := thread_node.ThreadHandle()
|
|
|
|
for _, info := range(thread.Children) {
|
|
if info.Child.ID() == principal.ID() {
|
|
return policy.Actions.Allows(resource, action)
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
type UserOfPolicy struct {
|
|
SimplePolicy
|
|
Target GroupNode
|
|
}
|
|
|
|
type UserOfPolicyJSON struct {
|
|
SimplePolicyJSON
|
|
Target string `json:"target"`
|
|
}
|
|
|
|
func (policy *UserOfPolicy) Type() NodeType {
|
|
return NodeType("user_of_policy")
|
|
}
|
|
|
|
func (policy *UserOfPolicy) Serialize() ([]byte, error) {
|
|
target := ""
|
|
if policy.Target != nil {
|
|
target = policy.Target.ID().String()
|
|
}
|
|
return json.MarshalIndent(&UserOfPolicyJSON{
|
|
SimplePolicyJSON: NewSimplePolicyJSON(&policy.SimplePolicy),
|
|
Target: target,
|
|
}, "", " ")
|
|
}
|
|
|
|
func NewUserOfPolicy(id NodeID, actions NodeActions) UserOfPolicy {
|
|
return UserOfPolicy{
|
|
SimplePolicy: NewSimplePolicy(id, actions),
|
|
Target: nil,
|
|
}
|
|
}
|
|
|
|
var LoadUserOfPolicy = LoadJSONNode(func(id NodeID, j UserOfPolicyJSON) (Node, error) {
|
|
policy := NewUserOfPolicy(id, j.Actions)
|
|
return &policy, nil
|
|
}, func(ctx *Context, policy *UserOfPolicy, j UserOfPolicyJSON, nodes NodeMap) error {
|
|
if j.Target != "" {
|
|
target_id, err := ParseID(j.Target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
target_node, err := LoadNodeRecurse(ctx, target_id, nodes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
target, ok := target_node.(GroupNode)
|
|
if ok == false {
|
|
return fmt.Errorf("%s is not a GroupNode", target_node.ID())
|
|
}
|
|
policy.Target = target
|
|
return nil
|
|
}
|
|
return RestoreSimpleNode(ctx, policy, j.SimpleNodeJSON, nodes)
|
|
})
|
|
|
|
func (policy *UserOfPolicy) Allows(context *StateContext, node Node, resource string, action string, principal Node) bool {
|
|
if policy.Target != nil {
|
|
allowed := false
|
|
err := UseStates(context, policy.Target, NewLockInfo(policy.Target, []string{"users"}), func(context *StateContext) error {
|
|
for _, user := range(policy.Target.Users()) {
|
|
if user.ID() == principal.ID() {
|
|
allowed = policy.Actions.Allows(resource, action)
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return allowed
|
|
}
|
|
return false
|
|
}
|