admission 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,94 @@
1
+ # module Admission::Rails
2
+ #
3
+ # class ActionsAdmission
4
+ #
5
+ # attr_reader :resource_actions, :resource_loader
6
+ #
7
+ # def initialize controller_class
8
+ # @controller_class = controller_class
9
+ # end
10
+ #
11
+ # def authorize prepend:false, **options
12
+ # callback_name = prepend ? :prepend_before_action : :before_action
13
+ # options = options.slice(:only, :except, :if, :unless)
14
+ # admission = self
15
+ #
16
+ # @controller_class.send callback_name, options do |controller|
17
+ # ActionAbility.new(controller, admission).authorize!
18
+ # end
19
+ # end
20
+ #
21
+ # def resource_actions *actions, loader:nil
22
+ # @resource_actions = actions.flatten
23
+ # @resource_loader = loader || :"find_#{@controller_class.controller_name.singularize}"
24
+ # end
25
+ #
26
+ # def is_resource_action? action
27
+ # @resource_actions.present? && @resource_actions.include?(action)
28
+ # end
29
+ #
30
+ # end
31
+ #
32
+ # class ActionAbility
33
+ # attr_reader :controller
34
+ #
35
+ # def initialize controller, admission
36
+ # @controller = controller
37
+ # @admission = admission
38
+ # end
39
+ #
40
+ # def authorize!
41
+ # user.ability.can?(action_name, object) || (raise AccessDenied.new(self))
42
+ # end
43
+ #
44
+ # def user
45
+ # @controller.current_user
46
+ # end
47
+ #
48
+ # def object
49
+ # @object ||= if @admission.is_resource_action?(action_name)
50
+ # @controller.send @admission.resource_loader
51
+ # else
52
+ # @controller.class.controller_name
53
+ # end
54
+ # end
55
+ #
56
+ # def action_name
57
+ # @action_name ||= @controller.params[:action].to_sym
58
+ # end
59
+ #
60
+ # def object_to_s
61
+ # case object
62
+ # when String then ":#{object}"
63
+ # when ActiveRecord::Base
64
+ # "<#{object.class.name}>"
65
+ # else
66
+ # object.to_s
67
+ # end
68
+ # end
69
+ #
70
+ # end
71
+ #
72
+ # class AccessDenied < ::StandardError
73
+ # attr_reader :action_ability
74
+ #
75
+ # def initialize ability
76
+ # @action_ability = ability
77
+ # @message = "Nemáte oprávnění pro tuto akci. (#{ability.action_name} - #{ability.object_to_s})"
78
+ # end
79
+ #
80
+ # def to_s
81
+ # @message
82
+ # end
83
+ # end
84
+ #
85
+ # end
86
+ #
87
+ #
88
+ # ActionController::Base.instance_exec do
89
+ # def actions_admission &block
90
+ # @actions_admission ||= Admission::Rails::ActionsAdmission.new(self)
91
+ # @actions_admission.instance_exec &block if block
92
+ # @actions_admission
93
+ # end
94
+ # end
@@ -0,0 +1,112 @@
1
+ class Admission::ResourceArbitration < Admission::Arbitration
2
+
3
+ def initialize person, rules_index, request, scope_or_resource
4
+ @person = person
5
+ scope, @resource = scope_and_resource scope_or_resource
6
+ @rules_index = rules_index[scope] || {}
7
+ @request = request.to_sym
8
+ end
9
+
10
+ def make_decision from_rules, privilege
11
+ if from_rules
12
+ decision = from_rules[privilege]
13
+ if Proc === decision
14
+ if decision.instance_variable_get :@resource_arbiter
15
+ decision = @person.instance_exec @resource, *@context, &decision
16
+ else
17
+ decision = @person.instance_exec *@context, &decision
18
+ end
19
+ end
20
+
21
+ unless Admission::VALID_DECISION.include? decision
22
+ raise "invalid decision: #{decision}"
23
+ end
24
+
25
+ decision
26
+ end
27
+ end
28
+
29
+ def scope_and_resource scope_or_resource
30
+ if scope_or_resource.is_a? Symbol
31
+ [scope_or_resource]
32
+ else
33
+ [self.class.type_to_scope(scope_or_resource.class).to_sym, scope_or_resource]
34
+ end
35
+ end
36
+
37
+ def self.type_to_scope_resolution proc=nil, &block
38
+ @type_to_scope = proc || block
39
+ end
40
+
41
+ def self.type_to_scope type
42
+ scope = @type_to_scope && @type_to_scope.call(type)
43
+ scope ? scope.to_sym : :"#{type.name.downcase}s"
44
+ end
45
+
46
+ def self.nested_scope resource, scope
47
+ resource = type_to_scope resource unless resource.is_a? Symbol
48
+ "#{resource}:#{scope}".to_sym
49
+ end
50
+
51
+ class RulesBuilder < Admission::Arbitration::RulesBuilder
52
+
53
+ def allow scope, *actions, &block
54
+ raise "reserved action name #{Admission::ALL_ACTION}" if actions.include? Admission::ALL_ACTION
55
+ raise "invalid scope name" unless scope.respond_to? :to_sym
56
+ add_allowance_rule actions.flatten, (block || true), scope: scope.to_sym
57
+ end
58
+
59
+ def allow_all scope, &block
60
+ raise "invalid scope name" unless scope.respond_to? :to_sym
61
+ add_allowance_rule [Admission::ALL_ACTION], (block || true), scope: scope.to_sym
62
+ end
63
+
64
+ def forbid scope, *actions
65
+ raise "reserved action name #{Admission::ALL_ACTION}" if actions.include? Admission::ALL_ACTION
66
+ raise "invalid scope name" unless scope.respond_to? :to_sym
67
+ add_allowance_rule actions.flatten, :forbidden, scope: scope.to_sym
68
+ end
69
+
70
+ def allow_resource resource, *actions, &block
71
+ raise "reserved action name #{Admission::ALL_ACTION}" if actions.include? Admission::ALL_ACTION
72
+ raise "block not given" unless block
73
+ block.instance_variable_set :@resource_arbiter, true
74
+ scope = case resource
75
+ when Symbol then resource
76
+ when Array then nested_scope(*resource)
77
+ else type_to_scope(resource)
78
+ end
79
+ add_allowance_rule actions.flatten, block, scope: scope
80
+ end
81
+
82
+ def type_to_scope resource
83
+ Admission::ResourceArbitration.type_to_scope resource
84
+ end
85
+
86
+ def nested_scope resource, scope
87
+ Admission::ResourceArbitration.nested_scope resource, scope
88
+ end
89
+
90
+ def create_index
91
+ index_instance = @rules.reduce Hash.new do |index, allowance|
92
+ privilege = allowance[:privilege]
93
+ actions = allowance[:actions]
94
+ scope = allowance[:scope]
95
+ arbiter = allowance[:arbiter]
96
+
97
+ scope_index = (index[scope] ||= {})
98
+
99
+ actions.each do |action|
100
+ action_index = (scope_index[action] ||= {})
101
+ action_index[privilege] = arbiter
102
+ end
103
+
104
+ index
105
+ end
106
+
107
+ index_instance.freeze
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,38 @@
1
+ class Admission::Status
2
+
3
+ attr_reader :person, :privileges, :rules
4
+
5
+ def initialize person, privileges, rules, arbiter
6
+ @person = person
7
+ @privileges = (privileges.nil? || privileges.empty?) ? nil : privileges
8
+ @rules = rules
9
+ @arbiter = arbiter
10
+ end
11
+
12
+ def can? *args
13
+ return false unless @privileges
14
+ process_request @arbiter.new(person, rules, *args)
15
+ end
16
+
17
+ def cannot? *args
18
+ !can?(*args)
19
+ end
20
+
21
+ def request! *args
22
+ can?(*args) || begin
23
+ exception = Admission::Denied.new self, *args
24
+ yield exception if block_given?
25
+ raise exception
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def process_request arbitration
32
+ privileges.any? do |privilege|
33
+ arbitration.prepare_sitting *privilege.context
34
+ arbitration.rule_per_privilege(privilege).eql? true
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,3 @@
1
+ module Admission
2
+ VERSION = '0.1.6'
3
+ end
@@ -0,0 +1,2 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../test_context/index'
@@ -0,0 +1,119 @@
1
+ require_relative '_helper'
2
+
3
+ RSpec.describe 'actions_arbitrating' do
4
+
5
+ def arbitration request, context=nil
6
+ person = Person.new 'person', Person::MALE, [:czech]
7
+ arbitration = Admission::Arbitration.new person, ACTIONS_RULES, request
8
+ arbitration.prepare_sitting *context
9
+ arbitration
10
+ end
11
+
12
+ def privilege *args, context: nil
13
+ p = Admission::Privilege.get_from_order PRIVILEGES_ORDER, *args
14
+ p = p.dup_with_context context if context
15
+ p
16
+ end
17
+
18
+ def rule request, privilege
19
+ arbitration(request, privilege.context).rule_per_privilege privilege
20
+ end
21
+
22
+ it 'allows human to do anything' do
23
+ expect(
24
+ rule :anything, privilege(:human)
25
+ ).to eql(true)
26
+ end
27
+
28
+ it 'disallows woman to do anything' do
29
+ person = Person.new 'person', Person::FEMALE, [:czech]
30
+ arbitration = Admission::Arbitration.new person, ACTIONS_RULES, :anything
31
+ arbitration.prepare_sitting
32
+ expect(
33
+ arbitration.rule_per_privilege privilege(:human)
34
+ ).to eql(false)
35
+ end
36
+
37
+ it 'allow woman-count to do anything in her country' do
38
+ person = Person.new 'person', Person::FEMALE, [:czech]
39
+ arbitration = Admission::Arbitration.new person, ACTIONS_RULES, :anything
40
+ arbitration.prepare_sitting :czech
41
+ expect(
42
+ arbitration.rule_per_privilege privilege(:human, :count, context: [:czech])
43
+ ).to eql(true)
44
+ end
45
+
46
+ it 'allows only king to raise taxes' do
47
+ expect(
48
+ rule :raise_taxes, privilege(:human)
49
+ ).to eql(:forbidden)
50
+
51
+ expect(
52
+ rule :raise_taxes, privilege(:human, :count)
53
+ ).to eql(:forbidden)
54
+
55
+ expect(
56
+ rule :raise_taxes, privilege(:human, :king)
57
+ ).to eql(true)
58
+ end
59
+
60
+ it 'allows count and king to impose corvee in his countries' do
61
+ expect(
62
+ rule :impose_corvee,
63
+ privilege(:human, :count, context: [:czech])
64
+ ).to eql(true)
65
+
66
+ expect(
67
+ rule :impose_corvee,
68
+ privilege(:human, :king, context: [:czech])
69
+ ).to eql(true)
70
+ end
71
+
72
+ it 'forbids count and king to impose corvee outside his countries' do
73
+ expect(
74
+ rule :impose_corvee,
75
+ privilege(:human, :count, context: [:taiwan])
76
+ ).to eql(:forbidden)
77
+
78
+ expect(
79
+ rule :impose_corvee,
80
+ privilege(:human, :king, context: [:taiwan])
81
+ ).to eql(:forbidden)
82
+ end
83
+
84
+ it 'forbids any human to impose a draft' do
85
+ expect(
86
+ rule :impose_draft, privilege(:human)
87
+ ).to eql(:forbidden)
88
+
89
+ expect(
90
+ rule :impose_draft, privilege(:human, :count)
91
+ ).to eql(:forbidden)
92
+
93
+ expect(
94
+ rule :impose_draft, privilege(:human, :king)
95
+ ).to eql(:forbidden)
96
+ end
97
+
98
+ it 'allows lord to impose draft' do
99
+ expect(
100
+ rule :impose_draft,
101
+ privilege(:vassal, :lord)
102
+ ).to eql(true)
103
+ end
104
+
105
+ it 'forbids emperor to impose draft because of inheritance' do
106
+ expect(
107
+ rule :impose_draft,
108
+ privilege(:emperor)
109
+ ).to eql(:forbidden)
110
+ end
111
+
112
+ it 'allows emperor to act as god' do
113
+ expect(
114
+ rule :act_as_god,
115
+ privilege(:emperor)
116
+ ).to eql(true)
117
+ end
118
+
119
+ end
@@ -0,0 +1,246 @@
1
+ require_relative '_helper'
2
+
3
+ RSpec.describe 'resources_arbitrating' do
4
+
5
+ let(:person){ Person.new 'person', Person::MALE, [:czech] }
6
+ let(:female){ Person.new 'female', Person::FEMALE, [:czech] }
7
+
8
+ def arbitration scope, action, context=nil
9
+ arbitration = Admission::ResourceArbitration.new person, RESOURCE_RULES, action, scope
10
+ arbitration.prepare_sitting *context
11
+ arbitration
12
+ end
13
+
14
+ def privilege *args, context: nil
15
+ p = Admission::Privilege.get_from_order PRIVILEGES_ORDER, *args
16
+ p = p.dup_with_context context if context
17
+ p
18
+ end
19
+
20
+ def actions_rule action, privilege
21
+ arbitration(:actions, action, privilege.context).rule_per_privilege privilege
22
+ end
23
+
24
+ def rule scope, action, privilege
25
+ arbitration(scope, action, privilege.context).rule_per_privilege privilege
26
+ end
27
+
28
+ describe 'actions scope' do
29
+
30
+ it 'allows human to do anything' do
31
+ expect(
32
+ actions_rule :anything, privilege(:human)
33
+ ).to eql(true)
34
+ end
35
+
36
+ it 'disallows woman to do anything' do
37
+ arbitration = Admission::ResourceArbitration.new female, RESOURCE_RULES,
38
+ :anything, :actions
39
+ arbitration.prepare_sitting
40
+ expect(
41
+ arbitration.rule_per_privilege privilege(:human)
42
+ ).to eql(false)
43
+ end
44
+
45
+ it 'allow woman-count to do anything in her country' do
46
+ arbitration = Admission::ResourceArbitration.new female, RESOURCE_RULES,
47
+ :anything, :actions
48
+ arbitration.prepare_sitting :czech
49
+ expect(
50
+ arbitration.rule_per_privilege privilege(:human, :count, context: [:czech])
51
+ ).to eql(true)
52
+ end
53
+
54
+ it 'allows only king to raise taxes' do
55
+ expect(
56
+ actions_rule :raise_taxes, privilege(:human)
57
+ ).to eql(:forbidden)
58
+
59
+ expect(
60
+ actions_rule :raise_taxes, privilege(:human, :count)
61
+ ).to eql(:forbidden)
62
+
63
+ expect(
64
+ actions_rule :raise_taxes, privilege(:human, :king)
65
+ ).to eql(true)
66
+ end
67
+
68
+ it 'allows count and king to impose corvee in his countries' do
69
+ expect(
70
+ actions_rule :impose_corvee,
71
+ privilege(:human, :count, context: [:czech])
72
+ ).to eql(true)
73
+
74
+ expect(
75
+ actions_rule :impose_corvee,
76
+ privilege(:human, :king, context: [:czech])
77
+ ).to eql(true)
78
+ end
79
+
80
+ it 'forbids count and king to impose corvee outside his countries' do
81
+ expect(
82
+ actions_rule :impose_corvee,
83
+ privilege(:human, :count, context: [:taiwan])
84
+ ).to eql(:forbidden)
85
+
86
+ expect(
87
+ actions_rule :impose_corvee,
88
+ privilege(:human, :king, context: [:taiwan])
89
+ ).to eql(:forbidden)
90
+ end
91
+
92
+ it 'forbids any human to impose a draft' do
93
+ expect(
94
+ actions_rule :impose_draft, privilege(:human)
95
+ ).to eql(:forbidden)
96
+
97
+ expect(
98
+ actions_rule :impose_draft, privilege(:human, :count)
99
+ ).to eql(:forbidden)
100
+
101
+ expect(
102
+ actions_rule :impose_draft, privilege(:human, :king)
103
+ ).to eql(:forbidden)
104
+ end
105
+
106
+ it 'allows lord to impose draft' do
107
+ expect(
108
+ actions_rule :impose_draft, privilege(:vassal, :lord)
109
+ ).to eql(true)
110
+ end
111
+
112
+ it 'forbids emperor to impose draft because of inheritance' do
113
+ expect(
114
+ actions_rule :impose_draft, privilege(:emperor)
115
+ ).to eql(:forbidden)
116
+ end
117
+
118
+ it 'allows emperor to act as god' do
119
+ expect(
120
+ actions_rule :act_as_god, privilege(:emperor)
121
+ ).to eql(true)
122
+ end
123
+
124
+ end
125
+
126
+ describe 'resources scope' do
127
+
128
+ it 'allows vassal to see only himself' do
129
+ expect(
130
+ rule person, :show, privilege(:vassal)
131
+ ).to eql(true)
132
+
133
+ person = Person.new 'person', Person::FEMALE, [:czech]
134
+ expect(
135
+ rule person, :show, privilege(:vassal)
136
+ ).to eql(false)
137
+ end
138
+
139
+ it 'passes nil as argument if resource-arbiter accessed by name-scope' do
140
+ expect{
141
+ rule :persons, :show, privilege(:vassal)
142
+ }.to raise_error('person is nil')
143
+ end
144
+
145
+ it 'allows vassal to list persons only per his countries' do
146
+ expect(
147
+ rule :persons, :index, privilege(:vassal, context: [:czech])
148
+ ).to eql(true)
149
+
150
+ expect(
151
+ rule :persons, :index, privilege(:vassal, context: [:taiwan])
152
+ ).to eql(false)
153
+ end
154
+
155
+ it 'allows access scope-arbiter by resource' do
156
+ expect(
157
+ rule person, :index, privilege(:vassal, context: [:czech])
158
+ ).to eql(true)
159
+ end
160
+
161
+ it 'allows lord to see any person' do
162
+ expect(
163
+ rule person, :show, privilege(:vassal, :lord)
164
+ ).to eql(true)
165
+
166
+ expect(
167
+ rule female, :show, privilege(:vassal, :lord)
168
+ ).to eql(true)
169
+ end
170
+
171
+ it 'allows lord to list persons from his country' do
172
+ expect(
173
+ rule person, :index, privilege(:vassal, context: [:czech])
174
+ ).to eql(true)
175
+
176
+ expect(
177
+ rule :persons, :index, privilege(:vassal, context: [:czech])
178
+ ).to eql(true)
179
+
180
+ expect(
181
+ rule person, :index, privilege(:vassal, context: [:taiwan])
182
+ ).to eql(false)
183
+ end
184
+
185
+ it 'allows lord to update person that is from his country' do
186
+ expect(
187
+ rule female, :update,
188
+ privilege(:vassal, :lord, context: [:czech])
189
+ ).to eql(true)
190
+
191
+ expect(
192
+ rule female, :update, privilege(:vassal, :lord)
193
+ ).to eql(false)
194
+ end
195
+
196
+ it 'disallows lord to update person not from his country' do
197
+ female = Person.new 'person', Person::FEMALE, [:taiwan]
198
+
199
+ expect(
200
+ rule female, :update,
201
+ privilege(:vassal, :lord, context: [:czech])
202
+ ).to eql(false)
203
+
204
+ expect(
205
+ rule female, :update,
206
+ privilege(:vassal, :lord, context: [:taiwan])
207
+ ).to eql(false)
208
+ end
209
+
210
+ it 'ensures lord cannot update person accessing him by scope-name' do
211
+ expect(
212
+ rule :persons, :update, privilege(:vassal, :lord)
213
+ ).to eql(false)
214
+ end
215
+
216
+ it 'disallows vassal to update person' do
217
+ expect(
218
+ rule person, :update, privilege(:vassal, context: [:czech])
219
+ ).to eql(false)
220
+ end
221
+
222
+ it 'allows lord to destroy person from his country' do
223
+ female = Person.new 'person', Person::FEMALE, [:taiwan]
224
+
225
+ expect(
226
+ rule person, :destroy,
227
+ privilege(:vassal, :lord, context: [:czech])
228
+ ).to eql(true)
229
+
230
+ expect(
231
+ rule female, :destroy,
232
+ privilege(:vassal, :lord, context: [:czech])
233
+ ).to eql(false)
234
+ end
235
+
236
+ it 'disallows lord to destroy apache helicopter' do
237
+ helicopter = Person.new 'person', Person::APACHE_HELICOPTER, [:czech]
238
+ expect(
239
+ rule helicopter, :destroy,
240
+ privilege(:vassal, :lord, context: [:czech])
241
+ ).to eql(false)
242
+ end
243
+
244
+ end
245
+
246
+ end