Class: ViewModel::AccessControl::Tree
Overview
Defines an access control discipline for a given action against a tree of viewmodels.
Extends the basic AccessControl to offer different checking based on the view type and position in a viewmodel tree.
Access checks for each given node type are specified at class level as ‘ComposedAccessControl`s, using `view` blocks. Checks that apply to all node types are specified in an `always` block.
In addition, node types can be marked as a ‘root’. Root types may permit and veto access to their non-root tree descendents with the additional access checks ‘root_children_editable,visible_if!` and `root_children_ editable,visible_unless!`. The results of evaluating these checks on entry to the root node.object_id will be cached and used when evaluating `visible` and `editable` on children.
Defined Under Namespace
Classes: Node
Constant Summary
Constants included
from Callbacks
Callbacks::ALWAYS
Class Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#editable!, #visible!
Methods included from Callbacks
#ineligible, #run_callback, wrap_deserialize, wrap_serialize
Constructor Details
#initialize ⇒ Tree
Returns a new instance of Tree.
78
79
80
81
82
83
84
|
# File 'lib/view_model/access_control/tree.rb', line 78
def initialize
super()
@always_policy_instance = self.class::AlwaysPolicy.new(self)
@view_policy_instances = self.class.view_policies.each_with_object({}) { |(name, policy), h| h[name] = policy.new(self) }
@root_visibility_store = {}
@root_editability_store = {}
end
|
Class Attribute Details
.view_policies ⇒ Object
Returns the value of attribute view_policies.
19
20
21
|
# File 'lib/view_model/access_control/tree.rb', line 19
def view_policies
@view_policies
end
|
Class Method Details
.always(&block) ⇒ Object
52
53
54
|
# File 'lib/view_model/access_control/tree.rb', line 52
def always(&block)
self::AlwaysPolicy.instance_exec(&block)
end
|
.create_policy(view_name) ⇒ Object
58
59
60
61
62
63
64
65
66
67
|
# File 'lib/view_model/access_control/tree.rb', line 58
def create_policy(view_name)
policy = Class.new(Node)
mangled_name = view_name.tr('.', '_')
const_set(:"#{mangled_name}Policy", policy)
view_policies[view_name] = policy
policy.include_from(self::AlwaysPolicy)
policy
end
|
.find_or_create_policy(view_name) ⇒ Object
69
70
71
|
# File 'lib/view_model/access_control/tree.rb', line 69
def find_or_create_policy(view_name)
view_policies.fetch(view_name) { create_policy(view_name) }
end
|
.include_from(ancestor) ⇒ Object
32
33
34
35
36
37
38
39
40
41
42
43
44
|
# File 'lib/view_model/access_control/tree.rb', line 32
def include_from(ancestor)
unless ancestor < ViewModel::AccessControl::Tree
raise ArgumentError.new("Invalid ancestor: #{ancestor}")
end
@included_checkers << ancestor
self::AlwaysPolicy.include_from(ancestor::AlwaysPolicy)
ancestor.view_policies.each do |view_name, ancestor_policy|
policy = find_or_create_policy(view_name)
policy.include_from(ancestor_policy)
end
end
|
.inherited(subclass) ⇒ Object
21
22
23
24
|
# File 'lib/view_model/access_control/tree.rb', line 21
def inherited(subclass)
super
subclass.initialize_as_tree_access_control
end
|
.initialize_as_tree_access_control ⇒ Object
26
27
28
29
30
|
# File 'lib/view_model/access_control/tree.rb', line 26
def initialize_as_tree_access_control
@included_checkers = []
@view_policies = {}
const_set(:AlwaysPolicy, Class.new(Node))
end
|
.inspect ⇒ Object
73
74
75
|
# File 'lib/view_model/access_control/tree.rb', line 73
def inspect
"#{super}(checks:\n#{@view_policies.values.map(&:inspect).join("\n")}\n#{self::AlwaysPolicy.inspect}\nincluded checkers: #{@included_checkers})"
end
|
.view(view_name, &block) ⇒ Object
47
48
49
50
|
# File 'lib/view_model/access_control/tree.rb', line 47
def view(view_name, &block)
policy = find_or_create_policy(view_name)
policy.instance_exec(&block)
end
|
Instance Method Details
#cleanup_descendent_results(view) ⇒ Object
125
126
127
128
|
# File 'lib/view_model/access_control/tree.rb', line 125
def cleanup_descendent_results(view)
@root_visibility_store.delete(view.object_id)
@root_editability_store.delete(view.object_id)
end
|
#editable_check(traversal_env) ⇒ Object
91
92
93
|
# File 'lib/view_model/access_control/tree.rb', line 91
def editable_check(traversal_env)
policy_instance_for(traversal_env.view).editable_check(traversal_env)
end
|
#fetch_descendent_editability(view) ⇒ Object
106
107
108
109
110
|
# File 'lib/view_model/access_control/tree.rb', line 106
def fetch_descendent_editability(view)
@root_editability_store.fetch(view.object_id) do
raise RuntimeError.new("No root access control data recorded for root")
end
end
|
#fetch_descendent_visibility(view) ⇒ Object
119
120
121
122
123
|
# File 'lib/view_model/access_control/tree.rb', line 119
def fetch_descendent_visibility(view)
@root_visibility_store.fetch(view.object_id) do
raise RuntimeError.new("No root access control data recorded for root")
end
end
|
#store_descendent_editability(view, descendent_editability) ⇒ Object
99
100
101
102
103
104
|
# File 'lib/view_model/access_control/tree.rb', line 99
def store_descendent_editability(view, descendent_editability)
if @root_editability_store.has_key?(view.object_id)
raise RuntimeError.new("Root access control data already saved for root")
end
@root_editability_store[view.object_id] = descendent_editability
end
|
#store_descendent_visibility(view, descendent_visibility) ⇒ Object
112
113
114
115
116
117
|
# File 'lib/view_model/access_control/tree.rb', line 112
def store_descendent_visibility(view, descendent_visibility)
if @root_visibility_store.has_key?(view.object_id)
raise RuntimeError.new("Root access control data already saved for root")
end
@root_visibility_store[view.object_id] = descendent_visibility
end
|
#valid_edit_check(traversal_env) ⇒ Object
95
96
97
|
# File 'lib/view_model/access_control/tree.rb', line 95
def valid_edit_check(traversal_env)
policy_instance_for(traversal_env.view).valid_edit_check(traversal_env)
end
|
#visible_check(traversal_env) ⇒ Object
87
88
89
|
# File 'lib/view_model/access_control/tree.rb', line 87
def visible_check(traversal_env)
policy_instance_for(traversal_env.view).visible_check(traversal_env)
end
|