logical_authz 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/controllers/groups_controller.rb +77 -0
- data/app/controllers/groups_users_controller.rb +27 -0
- data/app/controllers/permissions_controller.rb +63 -0
- data/app/helpers/logical_authz_helper.rb +158 -0
- data/app/views/groups/_controls.html.haml +18 -0
- data/app/views/groups/_form.html.haml +4 -0
- data/app/views/groups/create.rjs +1 -0
- data/app/views/groups/edit.html.haml +1 -0
- data/app/views/groups/index.html.haml +14 -0
- data/app/views/groups/new.html.haml +2 -0
- data/app/views/groups/show.html.haml +6 -0
- data/app/views/permissions/_controls.html.haml +18 -0
- data/app/views/permissions/_form.html.haml +8 -0
- data/app/views/permissions/create.rjs +1 -0
- data/app/views/permissions/edit.html.haml +1 -0
- data/app/views/permissions/index.html.haml +20 -0
- data/app/views/permissions/new.html.haml +2 -0
- data/config/initializers/activate.rb +1 -0
- data/generators/logical_authz/logical_authz_generator.rb +13 -0
- data/generators/logical_authz/templates/README +11 -0
- data/generators/logical_authz/templates/app/controllers/authz_controller.rb.erb +4 -0
- data/generators/logical_authz/templates/app/views/layouts/_explain_authz.html.haml.erb +21 -0
- data/generators/logical_authz_models/logical_authz_models_generator.rb +22 -0
- data/generators/logical_authz_routes/logical_authz_routes_generator.rb +12 -0
- data/generators/logical_authz_specs/logical_authz_specs_generator.rb +26 -0
- data/lib/logical_authz/access_control.rb +343 -0
- data/lib/logical_authz/application.rb +350 -0
- data/lib/logical_authz/authn_facade/authlogic.rb +13 -0
- data/lib/logical_authz/configuration.rb +64 -0
- data/lib/logical_authz/engine.rb +18 -0
- data/lib/logical_authz/generator.rb +22 -0
- data/lib/logical_authz/generators/controllers/generator.rb +15 -0
- data/lib/logical_authz/generators/controllers/templates/app/controllers/authz_controller.rb +6 -0
- data/lib/logical_authz/generators/models/generator.rb +109 -0
- data/lib/logical_authz/generators/models/templates/app/models/group.rb +33 -0
- data/lib/logical_authz/generators/models/templates/app/models/permission.rb +3 -0
- data/lib/logical_authz/generators/models/templates/config/initializers/logical_authz.rb +20 -0
- data/lib/logical_authz/generators/models/templates/db/seeds_logical_authz.rb +21 -0
- data/lib/logical_authz/generators/models/templates/migrations/create_groups.rb +12 -0
- data/lib/logical_authz/generators/models/templates/migrations/create_permissions.rb +15 -0
- data/lib/logical_authz/generators/models/templates/migrations/create_users_groups.rb +13 -0
- data/lib/logical_authz/generators/routes/generator.rb +21 -0
- data/lib/logical_authz/generators/specs/generator.rb +57 -0
- data/lib/logical_authz/generators/specs/templates/spec/controllers/groups_controller_spec.rb +102 -0
- data/lib/logical_authz/generators/specs/templates/spec/controllers/groups_users_controller_spec.rb +47 -0
- data/lib/logical_authz/generators/specs/templates/spec/controllers/permissions_controller_spec.rb +24 -0
- data/lib/logical_authz/generators/specs/templates/spec/factories/az_accounts.rb +7 -0
- data/lib/logical_authz/generators/specs/templates/spec/factories/az_groups.rb +7 -0
- data/lib/logical_authz/generators/specs/templates/spec/factories/permissions.rb +2 -0
- data/lib/logical_authz/generators/specs/templates/spec/helpers/logical_authz_helper_spec.rb +90 -0
- data/lib/logical_authz/generators/specs/templates/spec/support/logical_authz.rb +1 -0
- data/lib/logical_authz/generators/specs/templates/spec/support/mock_auth.rb +30 -0
- data/lib/logical_authz/spec_helper.rb +75 -0
- data/lib/logical_authz.rb +110 -0
- data/lib/tasks/rspec.rake +15 -0
- data/spec/gem_test_suite.rb +17 -0
- data/spec/spec_helper.rb +43 -0
- metadata +127 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
module LogicalAuthz
|
|
2
|
+
module AccessControl
|
|
3
|
+
class PolicyDefinitionError < ::Exception; end
|
|
4
|
+
|
|
5
|
+
class Builder
|
|
6
|
+
class << self
|
|
7
|
+
def register_policy_class(name, klass)
|
|
8
|
+
define_method(name) { klass.new }
|
|
9
|
+
define_method("if_#{name}") { klass.new }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def register_policy_helper(name, &block)
|
|
13
|
+
define_method(name, &block)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(helper_mod = nil)
|
|
18
|
+
@helper_mod = helper_mod
|
|
19
|
+
@list = @before = []
|
|
20
|
+
@after = []
|
|
21
|
+
|
|
22
|
+
(class << self; self; end).instance_eval do
|
|
23
|
+
include(helper_mod) unless helper_mod.nil?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def define(&block)
|
|
28
|
+
instance_eval(&block)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def resolve_rule(rule)
|
|
32
|
+
case rule
|
|
33
|
+
when Policy #This is the important case, actually
|
|
34
|
+
when Symbol, String
|
|
35
|
+
klass = Policy.names[rule.to_sym]
|
|
36
|
+
raise "Policy name #{rule} not found in #{Policy.names.keys.inspect}" if klass.nil?
|
|
37
|
+
Rails.logger.debug { "Using deprecated string/symbol policy naming: #{rule.inspect}" }
|
|
38
|
+
rule = klass.new
|
|
39
|
+
when Class
|
|
40
|
+
rule = rule.new
|
|
41
|
+
unless rule.responds_to?(:check)
|
|
42
|
+
raise "Policy classes must respond to #check"
|
|
43
|
+
end
|
|
44
|
+
when Proc
|
|
45
|
+
rule = ProcPolicy.new(&rule)
|
|
46
|
+
else
|
|
47
|
+
raise "Authorization Rules have to be Policy objects, a Policy class or a proc"
|
|
48
|
+
end
|
|
49
|
+
rule
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#TODO DSL needs to allow config of rules
|
|
53
|
+
def add_rule(rule, allows = true, name = nil)
|
|
54
|
+
rule = resolve_rule(rule)
|
|
55
|
+
|
|
56
|
+
rule.decision = allows
|
|
57
|
+
rule.name = name unless name.nil?
|
|
58
|
+
@list << rule
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def allow(rule = nil, name = nil, &block)
|
|
62
|
+
if rule.nil?
|
|
63
|
+
if block.nil?
|
|
64
|
+
raise "Allow needs to have a rule or a block"
|
|
65
|
+
end
|
|
66
|
+
rule = block
|
|
67
|
+
end
|
|
68
|
+
add_rule(rule, true, name)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def deny(rule = nil, name = nil, &block)
|
|
72
|
+
if rule.nil?
|
|
73
|
+
if block.nil?
|
|
74
|
+
raise "Deny needs to have a rule or a block"
|
|
75
|
+
end
|
|
76
|
+
rule = block
|
|
77
|
+
end
|
|
78
|
+
add_rule(rule, false, name)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def if_allowed(&block)
|
|
82
|
+
IfAllows.new(@helper_mod, &block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def if_denied(&block)
|
|
86
|
+
IfDenies.new(@helper_mod, &block)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def related(&block)
|
|
90
|
+
raise PolicyDefinitionError, "related called without a block" if block.nil?
|
|
91
|
+
Owner.new(&block)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def except(policy) #This needs a different name
|
|
95
|
+
policy = resolve_rule(policy)
|
|
96
|
+
Reversed.new(policy)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def with_criteria(policy, &block)
|
|
100
|
+
raise PolicyDefinitionError, "with_criteria called without a block" if block.nil?
|
|
101
|
+
policy = resolve_rule(policy)
|
|
102
|
+
RemappedCriteria.new(policy, &block)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def existing_policy
|
|
106
|
+
@list = @after
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def list(existing = nil)
|
|
110
|
+
existing ||= []
|
|
111
|
+
result = @before + existing + @after
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class Policy
|
|
116
|
+
def initialize
|
|
117
|
+
@decision = false
|
|
118
|
+
@name = default_name
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def laz_debug
|
|
122
|
+
LogicalAuthz::laz_debug{yield} if block_given?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
attr_accessor :name, :decision
|
|
126
|
+
|
|
127
|
+
def default_name
|
|
128
|
+
"Unknown Rule"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def check(criteria)
|
|
132
|
+
raise NotImplementedException
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def evaluate(criteria)
|
|
136
|
+
laz_debug{"Rule being examined: #{self.inspect}"}
|
|
137
|
+
if check(criteria) == true
|
|
138
|
+
laz_debug{"Rule: #@name triggered - authorization allowed: #@decision"}
|
|
139
|
+
return @decision
|
|
140
|
+
else
|
|
141
|
+
return nil
|
|
142
|
+
end
|
|
143
|
+
rescue Object => ex
|
|
144
|
+
Rails.logger.info{ "Exception raised checking rule \"#@name\": #{ex.class.name}: #{ex.message} @ #{ex.backtrace[0..2].inspect}" }
|
|
145
|
+
return false
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class << self
|
|
149
|
+
def names
|
|
150
|
+
@names ||= {}
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def register(name)
|
|
154
|
+
Policy.names[name.to_sym] = self
|
|
155
|
+
Policy.names["if_#{name}".to_sym] = self
|
|
156
|
+
|
|
157
|
+
AccessControl::Builder.register_policy_class(name, self)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
#The policy rule of last resort
|
|
163
|
+
class ProcPolicy < Policy
|
|
164
|
+
def initialize(&check)
|
|
165
|
+
@check = check
|
|
166
|
+
super()
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def check(criteria)
|
|
170
|
+
@check.call(criteria)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
class Always < Policy
|
|
175
|
+
register :always
|
|
176
|
+
|
|
177
|
+
def default_name
|
|
178
|
+
"Always"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def check(criteria)
|
|
182
|
+
true
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class Reversed < Policy
|
|
187
|
+
def initialize(other)
|
|
188
|
+
@other = other
|
|
189
|
+
super()
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def default_name
|
|
193
|
+
"Unless: #{@other.default_name}"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def check(criteria)
|
|
197
|
+
!@other.check(criteria)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
class RemappedCriteria < Policy
|
|
202
|
+
def initialize(other, &block)
|
|
203
|
+
@other = other
|
|
204
|
+
@block = block
|
|
205
|
+
super()
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def default_name
|
|
209
|
+
"Remapped: #{@other.default_name}"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def check(criteria)
|
|
213
|
+
new_criteria = criteria.dup
|
|
214
|
+
laz_debug{ {:Remapping => new_criteria} }
|
|
215
|
+
@block.call(new_criteria)
|
|
216
|
+
laz_debug{ {:Remappped => new_criteria} }
|
|
217
|
+
@other.check(new_criteria)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
class SubPolicy < Policy
|
|
222
|
+
def initialize(helper_mod, &block)
|
|
223
|
+
super()
|
|
224
|
+
builder = Builder.new(helper_mod)
|
|
225
|
+
builder.define(&block)
|
|
226
|
+
@criteria_list = builder.list
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def check(criteria)
|
|
230
|
+
@criteria_list.each do |control|
|
|
231
|
+
policy = control.evaluate(criteria)
|
|
232
|
+
next if policy.nil?
|
|
233
|
+
return match_policy(policy)
|
|
234
|
+
end
|
|
235
|
+
return false
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
class IfAllows < SubPolicy
|
|
240
|
+
def default_name
|
|
241
|
+
"If allowed by..."
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def match_policy(policy)
|
|
245
|
+
policy == true
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
class IfDenies < SubPolicy
|
|
250
|
+
def default_name
|
|
251
|
+
"If denied by..."
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def match_policy(policy)
|
|
255
|
+
policy == false
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
class Administrator < Policy
|
|
260
|
+
register :admin
|
|
261
|
+
|
|
262
|
+
def default_name
|
|
263
|
+
"Admins"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def check(criteria)
|
|
267
|
+
return criteria[:group].include?(Group.admin_group)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
class Authenticated < Policy
|
|
272
|
+
register :authenticated
|
|
273
|
+
|
|
274
|
+
def default_name
|
|
275
|
+
"Authenicated"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def check(criteria)
|
|
279
|
+
criteria[:user] != nil
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
class Authorized < Policy
|
|
284
|
+
register :authorized
|
|
285
|
+
|
|
286
|
+
def default_name
|
|
287
|
+
"When Authorized"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
#This probably needs some assurance that it cannot loop
|
|
291
|
+
def check(criteria)
|
|
292
|
+
criteria[:authorization_depth] ||= 0
|
|
293
|
+
criteria[:authorization_depth] += 1
|
|
294
|
+
|
|
295
|
+
if criteria[:authorization_depth] > 10
|
|
296
|
+
raise "Authorization recursion limit reached"
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
LogicalAuthz.is_authorized?(criteria)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
class Owner < Policy
|
|
304
|
+
register :owner
|
|
305
|
+
|
|
306
|
+
def initialize(&map_owner)
|
|
307
|
+
@mapper = map_owner
|
|
308
|
+
super()
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def default_name
|
|
312
|
+
"Related"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def check(criteria)
|
|
316
|
+
return false unless criteria.has_key?(:user) and criteria.has_key?(:id)
|
|
317
|
+
unless @mapper.nil?
|
|
318
|
+
@mapper.call(criteria[:user], criteria[:id].to_i)
|
|
319
|
+
else
|
|
320
|
+
criteria[:user].id == criteria[:id].to_i
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
class Permitted < Policy
|
|
326
|
+
register :permitted
|
|
327
|
+
|
|
328
|
+
def initialize(specific_criteria = {})
|
|
329
|
+
@criteria = specific_criteria
|
|
330
|
+
super()
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def default_name
|
|
334
|
+
"Permitted"
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def check(criteria)
|
|
338
|
+
crits = criteria.merge(@criteria)
|
|
339
|
+
return LogicalAuthz::check_permitted(crits)
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
require 'logical_authz_helper'
|
|
2
|
+
|
|
3
|
+
module LogicalAuthz
|
|
4
|
+
module Application
|
|
5
|
+
def self.included(klass)
|
|
6
|
+
klass.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
include Helper
|
|
9
|
+
|
|
10
|
+
def redirect_to_lobby(message = nil)
|
|
11
|
+
back = request.headers["Referer"]
|
|
12
|
+
laz_debug{"Sending user back to: #{back} Authz'd?"}
|
|
13
|
+
back_criteria = criteria_from_url(back)
|
|
14
|
+
if back_criteria.nil?
|
|
15
|
+
laz_debug{"Back is nil - going to the default_unauthorized_url"}
|
|
16
|
+
redirect_to default_unauthorized_url
|
|
17
|
+
elsif LogicalAuthz::is_authorized?(back_criteria)
|
|
18
|
+
laz_debug{"Back authorized - going to #{back}"}
|
|
19
|
+
redirect_to back
|
|
20
|
+
else
|
|
21
|
+
laz_debug{"Back is unauthorized - going to the default_unauthorized_url"}
|
|
22
|
+
redirect_to default_unauthorized_url
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def strip_record(record)
|
|
27
|
+
{
|
|
28
|
+
:rule => record[:determining_rule].try(:name),
|
|
29
|
+
:logged_in => !record[:user].nil?,
|
|
30
|
+
:reason => record[:reason],
|
|
31
|
+
:result => record[:result]
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def check_authorized
|
|
36
|
+
current_user = AuthnFacade.current_user(self)
|
|
37
|
+
|
|
38
|
+
criteria = {
|
|
39
|
+
:user => current_user,
|
|
40
|
+
:controller => self.class,
|
|
41
|
+
:action => action_name,
|
|
42
|
+
:id => params[:id],
|
|
43
|
+
:params => params.dup
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
logical_authz_record = {:authz_path => request.path.dup}
|
|
47
|
+
LogicalAuthz.is_authorized?(criteria, logical_authz_record)
|
|
48
|
+
laz_debug{"Logical Authz result: #{logical_authz_record.inspect}"}
|
|
49
|
+
flash[:logical_authz_record] = strip_record(logical_authz_record)
|
|
50
|
+
if logical_authz_record[:result]
|
|
51
|
+
return true
|
|
52
|
+
else
|
|
53
|
+
request.session[:unauthzd_path] = request.path
|
|
54
|
+
redirect_to_lobby("Your account is not authorized to perform this action.")
|
|
55
|
+
return false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
module ClassMethods
|
|
60
|
+
def laz_debug
|
|
61
|
+
LogicalAuthz::laz_debug{yield} if block_given?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def publicly_allowed(*actions)
|
|
65
|
+
if actions.empty?
|
|
66
|
+
authorization_by_default(true)
|
|
67
|
+
reset_policy
|
|
68
|
+
else
|
|
69
|
+
reset_policy(*actions)
|
|
70
|
+
policy(*actions) do |pol|
|
|
71
|
+
allow :always
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def policy_helper_module
|
|
77
|
+
@policy_helper_module ||=
|
|
78
|
+
begin
|
|
79
|
+
mod = Module.new
|
|
80
|
+
parent_mod = read_inheritable_attribute(:policy_helper_module)
|
|
81
|
+
unless parent_mod.nil?
|
|
82
|
+
mod.class_eval {
|
|
83
|
+
include(parent_mod)
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
write_inheritable_attribute(:policy_helper_module, mod)
|
|
87
|
+
mod
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def policy_helper(name, &body)
|
|
92
|
+
policy_helper_module.module_eval do
|
|
93
|
+
define_method name, &body
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def policy(*actions, &block)
|
|
98
|
+
before_filter CheckAuthorization
|
|
99
|
+
builder = AccessControl::Builder.new(policy_helper_module)
|
|
100
|
+
builder.define(&block)
|
|
101
|
+
if actions.empty?
|
|
102
|
+
set_policy(builder.list(get_policy(nil)), nil)
|
|
103
|
+
else
|
|
104
|
+
actions = unalias_actions(actions)
|
|
105
|
+
actions.each do |action|
|
|
106
|
+
set_policy(builder.list(get_policy(action)), action)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def reset_policy(*actions)
|
|
112
|
+
if actions.empty?
|
|
113
|
+
set_policy([], nil)
|
|
114
|
+
else
|
|
115
|
+
unalias_actions(actions).each do |action|
|
|
116
|
+
set_policy([], action)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def clear_policies!
|
|
122
|
+
write_inheritable_attribute(:controller_access_control, [])
|
|
123
|
+
write_inheritable_attribute(:action_access_control, {})
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def get_policy(action)
|
|
127
|
+
if action.nil?
|
|
128
|
+
read_inheritable_attribute(:controller_access_control) || []
|
|
129
|
+
else
|
|
130
|
+
(read_inheritable_attribute(:action_access_control) || {})[action.to_sym]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def set_policy(acl, action)
|
|
135
|
+
if action.nil?
|
|
136
|
+
laz_debug{ "Policy set: #{self.name} - all: #{acl.inspect}" }
|
|
137
|
+
write_inheritable_attribute(:controller_access_control, acl)
|
|
138
|
+
else
|
|
139
|
+
laz_debug{ "Policy set: #{self.name}##{action}: #{acl.inspect}" }
|
|
140
|
+
write_inheritable_hash(:action_access_control, {})
|
|
141
|
+
policies = read_inheritable_attribute(:action_access_control)
|
|
142
|
+
policies[action.to_sym] = acl
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def authorization_by_default(default_allow)
|
|
147
|
+
write_inheritable_attribute(:authorization_policy, default_allow)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def default_authorization
|
|
151
|
+
policy = read_inheritable_attribute(:authorization_policy)
|
|
152
|
+
if policy.nil?
|
|
153
|
+
true
|
|
154
|
+
else
|
|
155
|
+
policy
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def authorization_needed?(action)
|
|
160
|
+
acl = access_controls(action)
|
|
161
|
+
return true unless acl.empty?
|
|
162
|
+
return !read_inheritable_attribute(:authorization_policy) || false
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def move_policies(from, to)
|
|
166
|
+
policies = read_inheritable_attribute(:action_access_control)
|
|
167
|
+
if policies.nil?
|
|
168
|
+
policies = {}
|
|
169
|
+
write_inheritable_attribute(:action_access_control, policies)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
if policies.has_key?(from.to_sym)
|
|
173
|
+
if policies.has_key?(to.to_sym)
|
|
174
|
+
#Should be raise, at some future point
|
|
175
|
+
warn "Moving policies defined on #{self.name} for #{from} would clobber policies on #{to}"
|
|
176
|
+
end
|
|
177
|
+
policies[to.to_sym] = policies[from.to_sym]
|
|
178
|
+
policies.delete(from.to_sym)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# grant_aliases :new => :create # =>
|
|
183
|
+
# anyone with authorization to :create can also access :new
|
|
184
|
+
# (Read as: "for 'new' read 'create'")
|
|
185
|
+
def grant_aliases(hash)
|
|
186
|
+
aliases = read_inheritable_attribute(:grant_alias_hash) || Hash.new{|h,k| h[k] = []}
|
|
187
|
+
aliased = read_inheritable_attribute(:aliased_grants) || {}
|
|
188
|
+
hash.each_pair do |grant, allows|
|
|
189
|
+
[*allows].each do |allowed|
|
|
190
|
+
aliases[allowed.to_sym] << grant.to_sym
|
|
191
|
+
aliased[grant.to_sym] = allowed.to_sym
|
|
192
|
+
move_policies(grant, allowed)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
write_inheritable_attribute(:grant_alias_hash, aliases)
|
|
196
|
+
write_inheritable_attribute(:aliased_grants, aliased)
|
|
197
|
+
end
|
|
198
|
+
alias grant_alias grant_aliases
|
|
199
|
+
|
|
200
|
+
def standard_grant_aliases
|
|
201
|
+
grant_aliases :edit => :update
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def unalias_actions(actions)
|
|
205
|
+
aliased_actions = read_inheritable_attribute(:aliased_grants) || {}
|
|
206
|
+
actions.compact.map do |action|
|
|
207
|
+
aliased_actions[action.to_sym] || action
|
|
208
|
+
end.compact.uniq
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def grant_aliases_for(action)
|
|
212
|
+
grant_aliases = read_inheritable_attribute(:grant_alias_hash)
|
|
213
|
+
action = action.to_sym
|
|
214
|
+
|
|
215
|
+
if not grant_aliases.nil? and grant_aliases.has_key?(action)
|
|
216
|
+
return grant_aliases[action]
|
|
217
|
+
else
|
|
218
|
+
return []
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def inspect_criteria(criteria)
|
|
223
|
+
criteria.inject({}) do |hash, name_value|
|
|
224
|
+
name, value = *name_value
|
|
225
|
+
case value
|
|
226
|
+
when ActiveRecord::Base
|
|
227
|
+
hash[name] = {value.class.name => value.id}
|
|
228
|
+
when ActionController::Base
|
|
229
|
+
hash[name] = value.class
|
|
230
|
+
else
|
|
231
|
+
hash[name] = value
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
hash
|
|
235
|
+
end.inspect
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def normalize_criteria(criteria)
|
|
239
|
+
criteria[:group] = criteria[:group].nil? ? [] : [*criteria[:group]]
|
|
240
|
+
if criteria.has_key?(:user) and not criteria[:user].nil?
|
|
241
|
+
criteria[:group] += criteria[:user].groups
|
|
242
|
+
end
|
|
243
|
+
#if criteria[:group].empty?
|
|
244
|
+
# criteria[:group] += LogicalAuthz::unauthorized_groups
|
|
245
|
+
#end
|
|
246
|
+
criteria[:group], not_groups = criteria[:group].partition do |group|
|
|
247
|
+
LogicalAuthz::Configuration::group_model === group
|
|
248
|
+
end
|
|
249
|
+
Rails.logger.warn{ "Found in criteria[:groups]: #{not_groups.inspect}"} unless not_groups.empty?
|
|
250
|
+
actions = [*criteria[:action]].compact
|
|
251
|
+
criteria[:action_aliases] = actions.map do |action|
|
|
252
|
+
grant_aliases_for(action)
|
|
253
|
+
end.flatten + actions.map{|action| action.to_sym}
|
|
254
|
+
|
|
255
|
+
criteria[:controller] = self
|
|
256
|
+
criteria[:controller_path] = controller_path
|
|
257
|
+
|
|
258
|
+
laz_debug{"LogicalAuthz: final computed authz criteria: #{inspect_criteria(criteria)}"}
|
|
259
|
+
|
|
260
|
+
return criteria
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def access_controls(action)
|
|
264
|
+
controller_acl = read_inheritable_attribute(:controller_access_control) || []
|
|
265
|
+
return controller_acl if action.nil?
|
|
266
|
+
action = unalias_actions([action]).first
|
|
267
|
+
action_acl = (read_inheritable_attribute(:action_access_control) || {})[action.to_sym] || []
|
|
268
|
+
laz_debug{ { :checking_policy_for => action, :policies_exist_for => (read_inheritable_attribute(:action_access_control) || {}).keys, :action_acl => action_acl, :controller_acl => controller_acl } }
|
|
269
|
+
action_acl + controller_acl
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def check_acls(criteria, result_hash = nil)
|
|
273
|
+
result_hash ||= {}
|
|
274
|
+
policy = nil
|
|
275
|
+
|
|
276
|
+
acl = access_controls(criteria[:action])
|
|
277
|
+
result_hash.merge! :checked_rules => [], :determining_rule => nil, :all_rules => acl
|
|
278
|
+
acl.each do |control|
|
|
279
|
+
result_hash[:checked_rules] << control
|
|
280
|
+
policy = control.evaluate(criteria)
|
|
281
|
+
unless policy.nil?
|
|
282
|
+
laz_debug{"Rule triggered - result: #{policy.inspect}"}
|
|
283
|
+
result_hash.merge! :determining_rule => control, :reason => :rule_triggered, :result => policy
|
|
284
|
+
break
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
return policy
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
#It was tempting to build this on before_filter directly - however,
|
|
291
|
+
#inspecting a controller to see if a particular filter will run for a
|
|
292
|
+
#particular action is fragile.
|
|
293
|
+
def needs_authorization(*actions)
|
|
294
|
+
policy(*actions) do
|
|
295
|
+
allow if_allowed {
|
|
296
|
+
deny :authenticated
|
|
297
|
+
allow AccessControl::Permitted.new({:group => LogicalAuthz::Configuration.unauthorized_groups})
|
|
298
|
+
}
|
|
299
|
+
allow :permitted
|
|
300
|
+
existing_policy
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
policy do
|
|
304
|
+
existing_policy
|
|
305
|
+
deny :always
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
alias authorized_if_permitted needs_authorization
|
|
310
|
+
|
|
311
|
+
#This method exists for backwards compatibility. It's likely more
|
|
312
|
+
#readable to use the policy DSL
|
|
313
|
+
def dynamic_authorization(&block)
|
|
314
|
+
policy do |pol|
|
|
315
|
+
allow(&block)
|
|
316
|
+
existing_policy
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
#This method exists for backwards compatibility. It's likely more
|
|
321
|
+
#readable to use the policy DSL
|
|
322
|
+
def owner_authorized(*actions, &block)
|
|
323
|
+
policy(*actions) do |pol|
|
|
324
|
+
allow AccessControl::Owner.new(&block)
|
|
325
|
+
existing_policy
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
#This method exists for backwards compatibility. It's likely more
|
|
330
|
+
#readable to use the policy DSL
|
|
331
|
+
def admin_authorized(*actions)
|
|
332
|
+
policy(*actions) do |pol|
|
|
333
|
+
allow :if_admin
|
|
334
|
+
existing_policy
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
class CheckAuthorization
|
|
340
|
+
def self.filter(controller)
|
|
341
|
+
if controller.class.authorization_needed?(controller.action_name)
|
|
342
|
+
return controller.check_authorized
|
|
343
|
+
else
|
|
344
|
+
Rails.logger.debug{"Logical Authorization: #{controller} doesn't need authz"}
|
|
345
|
+
return true
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
if defined? LogicalAuthz::AuthnFacade
|
|
2
|
+
warn "logical_authz authentication facade (LogicalAuthz::AuthnFacade) already defined - not redefining."
|
|
3
|
+
else
|
|
4
|
+
module LogicalAuthz
|
|
5
|
+
class AuthnFacade
|
|
6
|
+
def self.current_user(controller)
|
|
7
|
+
#This assumes authlogic is implemented as recommended - otherwise
|
|
8
|
+
#you'll need to develop your own AuthnFacade
|
|
9
|
+
return controller.current_user
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|