declarative_authorization 0.3.2.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +46 -0
- data/app/helpers/authorization_rules_helper.rb +44 -13
- data/app/views/authorization_rules/_change.erb +3 -3
- data/app/views/authorization_rules/_suggestions.erb +2 -2
- data/app/views/authorization_rules/change.html.erb +19 -2
- data/app/views/authorization_rules/graph.dot.erb +3 -3
- data/lib/declarative_authorization/authorization.rb +70 -15
- data/lib/declarative_authorization/in_controller.rb +60 -38
- data/lib/declarative_authorization/maintenance.rb +13 -2
- data/lib/declarative_authorization/obligation_scope.rb +11 -3
- data/lib/declarative_authorization/reader.rb +25 -2
- data/test/authorization_test.rb +133 -3
- data/test/controller_test.rb +78 -0
- data/test/model_test.rb +187 -0
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,49 @@
|
|
1
|
+
** RELEASE 0.4 (November 15, 2009) **
|
2
|
+
|
3
|
+
* Implemented controller namespace handling [sb]
|
4
|
+
|
5
|
+
* Improved if_attribute to allow nesting of has_many associations [sb]
|
6
|
+
|
7
|
+
* Improved if_permitted_to: allow has_many associations and improved context inference [sb]
|
8
|
+
|
9
|
+
* Added option on handling non-existant auto-loaded object [sb]
|
10
|
+
|
11
|
+
* Added with_user as module method [sb]
|
12
|
+
|
13
|
+
* Change support i18n [sb]
|
14
|
+
|
15
|
+
** RELEASE 0.3.2.3 (October 12, 2009) **
|
16
|
+
|
17
|
+
* Switched to gemcutter [sb]
|
18
|
+
|
19
|
+
* Fixed has_role? for guest user. Closes #8 [sb]
|
20
|
+
|
21
|
+
* Fixed unnecessary DB query with named scopes [sb, ledermann]
|
22
|
+
|
23
|
+
* Change support: suggestions: grouping, sorting by affected users [sb]
|
24
|
+
|
25
|
+
* Fixed context inference from AR objects for STI by switching to #class.name.tableize [sb]
|
26
|
+
|
27
|
+
* Allow multiple contexts as arguments to has_permission_on [Jeroen van Dijk]
|
28
|
+
|
29
|
+
** RELEASE 0.3.2.2 (August 27, 2009) **
|
30
|
+
|
31
|
+
* Fix without_access_control test cases [sb]
|
32
|
+
|
33
|
+
* Fixed error on debug logging (Closes #6) [sb]
|
34
|
+
|
35
|
+
* Fixed without_access_control instance method in TestHelper [sb]
|
36
|
+
|
37
|
+
** RELEASE 0.3.2.1 (August 14, 2009) **
|
38
|
+
|
39
|
+
* Fix gemspec for Rdoc generation [sb]
|
40
|
+
|
41
|
+
** RELEASE 0.3.2 (August 13, 2009) **
|
42
|
+
|
43
|
+
* Fix for model-level permitted_to?/! [sb]
|
44
|
+
|
45
|
+
** RELEASE 0.3.1 (August 12, 2009) **
|
46
|
+
|
1
47
|
* Change Support: Suggestion grouping, sort by affected users [sb]
|
2
48
|
|
3
49
|
* Changed context derived from objects to #class.name.tableize to fix STI [sb]
|
@@ -84,32 +84,63 @@ module AuthorizationRulesHelper
|
|
84
84
|
role_color(role))
|
85
85
|
end
|
86
86
|
|
87
|
+
def human_privilege (privilege)
|
88
|
+
begin
|
89
|
+
I18n.t(privilege, :scope => [:declarative_authorization, :privilege], :raise => true)
|
90
|
+
rescue
|
91
|
+
privilege.to_s
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def human_context (context)
|
96
|
+
begin
|
97
|
+
context.to_s.classify.constantize.human_name
|
98
|
+
rescue
|
99
|
+
context.to_s
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def human_privilege_context (privilege, context)
|
104
|
+
human = [human_privilege(privilege), human_context(context)]
|
105
|
+
begin
|
106
|
+
unless I18n.t(:verb_in_front_of_object, :scope => :declarative_authorization, :raise => true)
|
107
|
+
human.reverse!
|
108
|
+
end
|
109
|
+
rescue
|
110
|
+
end
|
111
|
+
human * " "
|
112
|
+
end
|
113
|
+
|
114
|
+
def human_role (role)
|
115
|
+
Authorization::Engine.instance.title_for(role) or role.to_s
|
116
|
+
end
|
117
|
+
|
87
118
|
def describe_step (step, options = {})
|
88
119
|
options = {:with_removal => false}.merge(options)
|
89
120
|
|
90
121
|
case step[0]
|
91
122
|
when :add_privilege
|
92
123
|
dont_assign = prohibit_link(step[0,3],
|
93
|
-
"Add privilege <strong>#{h step[1]
|
94
|
-
"Don't suggest adding #{h step[1]
|
95
|
-
"Add privilege <strong>#{h step[1]
|
124
|
+
"Add privilege <strong>#{h human_privilege_context(step[1], step[2])}</strong> to any role",
|
125
|
+
"Don't suggest adding #{h human_privilege_context(step[1], step[2])}.", options)
|
126
|
+
"Add privilege <strong>#{h human_privilege_context(step[1], step[2])}</strong>#{dont_assign} to role <strong>#{h human_role(step[3].to_sym)}</strong>"
|
96
127
|
when :remove_privilege
|
97
128
|
dont_remove = prohibit_link(step[0,3],
|
98
|
-
"Remove privilege <strong>#{h step[1]
|
99
|
-
"Don't suggest removing #{h step[1]
|
100
|
-
"Remove privilege <strong>#{h step[1]
|
129
|
+
"Remove privilege <strong>#{h human_privilege_context(step[1], step[2])}</strong> from any role",
|
130
|
+
"Don't suggest removing #{h human_privilege_context(step[1], step[2])}.", options)
|
131
|
+
"Remove privilege <strong>#{h human_privilege_context(step[1], step[2])}</strong>#{dont_remove} from role <strong>#{h human_role(step[3].to_sym)}</strong>"
|
101
132
|
when :add_role
|
102
|
-
"New role <strong>#{h step[1].to_sym
|
133
|
+
"New role <strong>#{h human_role(step[1].to_sym)}</strong>"
|
103
134
|
when :assign_role_to_user
|
104
135
|
dont_assign = prohibit_link(step[0,2],
|
105
|
-
"Assign role <strong>#{h step[1].to_sym
|
106
|
-
"Don't suggest assigning #{h step[1].to_sym
|
107
|
-
"Assign role <strong>#{h step[1].to_sym
|
136
|
+
"Assign role <strong>#{h human_role(step[1].to_sym)}</strong> to any user",
|
137
|
+
"Don't suggest assigning #{h human_role(step[1].to_sym)}.", options)
|
138
|
+
"Assign role <strong>#{h human_role(step[1].to_sym)}</strong>#{dont_assign} to <strong>#{h readable_step_info(step[2])}</strong>"
|
108
139
|
when :remove_role_from_user
|
109
140
|
dont_remove = prohibit_link(step[0,2],
|
110
|
-
"Remove role <strong>#{h step[1].to_sym
|
111
|
-
"Don't suggest removing #{h step[1].to_sym
|
112
|
-
"Remove role <strong>#{h step[1].to_sym
|
141
|
+
"Remove role <strong>#{h human_role(step[1].to_sym)}</strong> from any user",
|
142
|
+
"Don't suggest removing #{h human_role(step[1].to_sym)}.", options)
|
143
|
+
"Remove role <strong>#{h human_role(step[1].to_sym)}</strong>#{dont_remove} from <strong>#{h readable_step_info(step[2])}</strong>"
|
113
144
|
else
|
114
145
|
step.collect {|info| readable_step_info(info) }.map {|str| h str } * ', '
|
115
146
|
end + prohibit_link(step, options[:with_removal] ? "#{escape_javascript(describe_step(step))}" : '',
|
@@ -2,10 +2,10 @@
|
|
2
2
|
<h2>Which permission to change?</h2>
|
3
3
|
<p class="action-options">
|
4
4
|
<label>Privilege</label>
|
5
|
-
<%= select_tag :privilege, options_for_select(@privileges.map(
|
5
|
+
<%= select_tag :privilege, options_for_select(@privileges.map {|p| [human_privilege(p), p.to_s]}.sort, @privilege.to_s) %>
|
6
6
|
<br/>
|
7
7
|
<label>On</label>
|
8
|
-
<%= select_tag :context, options_for_select(@contexts.map(
|
8
|
+
<%= select_tag :context, options_for_select(@contexts.map {|c| [human_context(c), c.to_s]}.sort, @context.to_s) %>
|
9
9
|
<br/>
|
10
10
|
<label></label>
|
11
11
|
<%= link_to_function "Show current permissions", "show_current_permissions()", :class => 'unimportant' %>
|
@@ -35,7 +35,7 @@
|
|
35
35
|
<tr class="<%= controller.authorization_engine.permit?(@privilege, :context => @context, :user => user, :skip_attribute_test => true) ? 'permitted' : 'not-permitted' %>">
|
36
36
|
<td class="user_id"><%=h user.id %></td>
|
37
37
|
<td style="font-weight:bold"><%=h user.login %></td>
|
38
|
-
<td><%=h user.role_symbols * ', ' %></td>
|
38
|
+
<td><%=h user.role_symbols.map {|r| human_role(r)} * ', ' %></td>
|
39
39
|
<td><%= radio_button_tag "user[#{user.id}][permission]", "undetermined", true %></td>
|
40
40
|
<td class="yes"><%= radio_button_tag "user[#{user.id}][permission]", "yes" %></td>
|
41
41
|
<td class="no"><%= radio_button_tag "user[#{user.id}][permission]", "no" %></td>
|
@@ -30,13 +30,13 @@
|
|
30
30
|
<% if grouped_approach.similar_approaches.empty? %>
|
31
31
|
<span class="unimportant show-others-in-group">No further suggestions in this group</span>
|
32
32
|
<% else %>
|
33
|
-
<%= link_to_function "Show further
|
33
|
+
<%= link_to_function "Show further <strong>#{pluralize grouped_approach.similar_approaches.length, "suggestion"}</strong> in this group", "show_group_approaches(#{group_index})", :class => "show-others-in-group" %>
|
34
34
|
<% end %>
|
35
35
|
<% end %>
|
36
36
|
<%= link_to_function "Hide further suggestions", "hide_group_approaches(#{group_index})" if index > 0 and index == grouped_approach.similar_approaches.length %>
|
37
37
|
</li>
|
38
38
|
<% if index == 0 and group_index == 2 and @grouped_approaches.length > 3 %>
|
39
|
-
<li <%= !params[:show_all] ? '' : 'style="display: none"' %>><%= link_to_function "Show further
|
39
|
+
<li <%= !params[:show_all] ? '' : 'style="display: none"' %>><%= link_to_function "Show further <strong>#{pluralize(@grouped_approaches.length - 3, 'group')}</strong>", "show_all_groups(); $(this).up().hide()" %></li>
|
40
40
|
<% end %>
|
41
41
|
<% end %>
|
42
42
|
<% end %>
|
@@ -8,6 +8,8 @@
|
|
8
8
|
</div>
|
9
9
|
<%= render 'show_graph' %>
|
10
10
|
|
11
|
+
<div id="spinner" style="display:none">Searching...</div>
|
12
|
+
|
11
13
|
<style type="text/css">
|
12
14
|
.action-options label { display: block; float: left; width: 7em; padding-bottom: 0.5em }
|
13
15
|
.action-options label.inline { display: inline; float: none }
|
@@ -52,17 +54,32 @@
|
|
52
54
|
.unimportant, .remove, .prohibit { color: grey }
|
53
55
|
.important { font-weight: bold }
|
54
56
|
.remove { cursor: pointer }
|
57
|
+
#spinner { position: fixed; top: 0; right: 0; background: #FFE599; padding: 1em }
|
55
58
|
</style>
|
56
59
|
<% javascript_tag do %>
|
57
60
|
function suggest_changes (refresh) {
|
58
|
-
|
61
|
+
var opened_groups = $$("#suggest-result>ul>li.secondary").select(function (li) {return li.visible()}).collect(function (li) {
|
62
|
+
return li.classNames().toArray()[1].split("-")[1];
|
63
|
+
}).uniq();
|
64
|
+
|
65
|
+
if (refresh)
|
66
|
+
$("spinner").show();
|
67
|
+
else
|
59
68
|
$("suggest-result").innerHTML = "Searching...";
|
69
|
+
|
70
|
+
|
60
71
|
$("suggest-result").show();
|
61
72
|
new Ajax.Updater({success: 'suggest-result'}, '<%= suggest_change_authorization_rules_path %>', {
|
62
73
|
method: 'get',
|
63
74
|
onFailure: function(request) {
|
64
75
|
$("suggest-result").innerHTML = "Error while searching."
|
65
76
|
},
|
77
|
+
onComplete: function () {
|
78
|
+
$("spinner").hide();
|
79
|
+
opened_groups.each(function (group_no) {
|
80
|
+
show_group_approaches(group_no);
|
81
|
+
});
|
82
|
+
},
|
66
83
|
parameters: $H($('change').down('form').serialize(true)).merge(refresh ? {show_all: "true"} : {}).toQueryString()
|
67
84
|
});
|
68
85
|
if (!refresh)
|
@@ -70,7 +87,7 @@
|
|
70
87
|
}
|
71
88
|
|
72
89
|
function show_suggest_graph (changes, filter_roles_params, filter_context, user_ids) {
|
73
|
-
var params = {changes: changes, highlight_privilege: $F('privilege')};
|
90
|
+
var params = {changes: changes, highlight_privilege: $F('privilege'), stacked_roles: '1'};
|
74
91
|
if (filter_context)
|
75
92
|
params['filter_contexts'] = filter_context;
|
76
93
|
if (user_ids)
|
@@ -22,7 +22,7 @@ digraph rules {
|
|
22
22
|
node [shape=ellipse,style=filled]
|
23
23
|
<%= @stacked_roles ? '' : "rank = same" %>
|
24
24
|
<% @roles.each do |role| %>
|
25
|
-
"<%= role.inspect %>" [fillcolor="<%= role_fill_color(role) %>"]
|
25
|
+
"<%= role.inspect %>" [fillcolor="<%= role_fill_color(role) %>",label="<%= human_role(role) %>"]
|
26
26
|
// ,URL="javascript:set_filter({roles: '<%= role %>'})"
|
27
27
|
<% end %>
|
28
28
|
<% @roles.each do |role| %>
|
@@ -42,11 +42,11 @@ digraph rules {
|
|
42
42
|
|
43
43
|
<% @contexts.each do |context| %>
|
44
44
|
subgraph cluster_<%= context %> {
|
45
|
-
label = "<%= context
|
45
|
+
label = "<%= human_context(context) %>"
|
46
46
|
style=filled; fillcolor="#eeeeee"
|
47
47
|
node[fillcolor=white,style=filled]
|
48
48
|
<% (@context_privs[context] || []).each do |priv| %>
|
49
|
-
<%= priv %>_<%= context %> [label="<%= priv
|
49
|
+
<%= priv %>_<%= context %> [label="<%= human_privilege(priv) %>"<%= ',fontcolor="#ff0000"' if @highlight_privilege == priv %>]
|
50
50
|
<% end %>
|
51
51
|
<% (@context_privs[context] || []).each do |priv| %>
|
52
52
|
<% (@privilege_hierarchy[priv] || []).
|
@@ -207,6 +207,9 @@ module Authorization
|
|
207
207
|
def obligations (privilege, options = {})
|
208
208
|
options = {:context => nil}.merge(options)
|
209
209
|
user, roles, privileges = user_roles_privleges_from_options(privilege, options)
|
210
|
+
|
211
|
+
permit!(privilege, :skip_attribute_test => true, :user => user, :context => options[:context])
|
212
|
+
|
210
213
|
attr_validator = AttributeValidator.new(self, user, nil, privilege, options[:context])
|
211
214
|
matching_auth_rules(roles, privileges, options[:context]).collect do |rule|
|
212
215
|
rule.obligations(attr_validator)
|
@@ -376,7 +379,20 @@ module Authorization
|
|
376
379
|
end
|
377
380
|
|
378
381
|
def obligations (attr_validator)
|
379
|
-
|
382
|
+
exceptions = []
|
383
|
+
obligations = @attributes.collect do |attr|
|
384
|
+
begin
|
385
|
+
attr.obligation(attr_validator)
|
386
|
+
rescue NotAuthorized => e
|
387
|
+
exceptions << e
|
388
|
+
nil
|
389
|
+
end
|
390
|
+
end.flatten.compact
|
391
|
+
|
392
|
+
if exceptions.length > 0 and (@join_operator == :and or exceptions.length == @attributes.length)
|
393
|
+
raise NotAuthorized, "Missing authorization in collecting obligations: #{exceptions.map(&:to_s) * ", "}"
|
394
|
+
end
|
395
|
+
|
380
396
|
if @join_operator == :and and !obligations.empty?
|
381
397
|
merged_obligation = obligations.first
|
382
398
|
obligations[1..-1].each do |obligation|
|
@@ -411,15 +427,17 @@ module Authorization
|
|
411
427
|
(hash || @conditions_hash).all? do |attr, value|
|
412
428
|
attr_value = object_attribute_value(object, attr)
|
413
429
|
if value.is_a?(Hash)
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
430
|
+
case attr_value
|
431
|
+
when Enumerable
|
432
|
+
attr_value.any? do |inner_value|
|
433
|
+
validate?(attr_validator, inner_value, value)
|
434
|
+
end
|
435
|
+
when nil
|
419
436
|
raise NilAttributeValueError, "Attribute #{attr.inspect} is nil in #{object.inspect}."
|
437
|
+
else
|
438
|
+
validate?(attr_validator, attr_value, value)
|
420
439
|
end
|
421
|
-
|
422
|
-
elsif value.is_a?(Array) and value.length == 2
|
440
|
+
elsif value.is_a?(Array) and value.length == 2 and value.first.is_a?(Symbol)
|
423
441
|
evaluated = if value[1].is_a?(Proc)
|
424
442
|
attr_validator.evaluate(value[1])
|
425
443
|
else
|
@@ -557,17 +575,29 @@ module Authorization
|
|
557
575
|
case hash_or_attr
|
558
576
|
when Symbol
|
559
577
|
attr_value = object_attribute_value(object, hash_or_attr)
|
560
|
-
|
578
|
+
case attr_value
|
579
|
+
when nil
|
561
580
|
raise NilAttributeValueError, "Attribute #{hash_or_attr.inspect} is nil in #{object.inspect}."
|
581
|
+
when Enumerable
|
582
|
+
attr_value.any? do |inner_value|
|
583
|
+
attr_validator.engine.permit? @privilege, :object => inner_value, :user => attr_validator.user
|
584
|
+
end
|
585
|
+
else
|
586
|
+
attr_validator.engine.permit? @privilege, :object => attr_value, :user => attr_validator.user
|
562
587
|
end
|
563
|
-
attr_validator.engine.permit? @privilege, :object => attr_value, :user => attr_validator.user
|
564
588
|
when Hash
|
565
589
|
hash_or_attr.all? do |attr, sub_hash|
|
566
590
|
attr_value = object_attribute_value(object, attr)
|
567
|
-
|
591
|
+
case attr_value
|
592
|
+
when nil
|
568
593
|
raise NilAttributeValueError, "Attribute #{attr.inspect} is nil in #{object.inspect}."
|
594
|
+
when Enumerable
|
595
|
+
attr_value.any? do |inner_value|
|
596
|
+
validate?(attr_validator, inner_value, sub_hash)
|
597
|
+
end
|
598
|
+
else
|
599
|
+
validate?(attr_validator, attr_value, sub_hash)
|
569
600
|
end
|
570
|
-
validate?(attr_validator, attr_value, sub_hash)
|
571
601
|
end
|
572
602
|
when NilClass
|
573
603
|
attr_validator.engine.permit? @privilege, :object => object, :user => attr_validator.user
|
@@ -577,20 +607,29 @@ module Authorization
|
|
577
607
|
end
|
578
608
|
|
579
609
|
# may return an array of obligations to be OR'ed
|
580
|
-
def obligation (attr_validator, hash_or_attr = nil)
|
610
|
+
def obligation (attr_validator, hash_or_attr = nil, path = [])
|
581
611
|
hash_or_attr ||= @attr_hash
|
582
612
|
case hash_or_attr
|
583
613
|
when Symbol
|
614
|
+
@context ||= begin
|
615
|
+
rule_model = attr_validator.context.to_s.classify.constantize
|
616
|
+
context_reflection = self.class.reflection_for_path(rule_model, path + [hash_or_attr])
|
617
|
+
context_reflection.klass.table_name.to_sym
|
618
|
+
rescue # missing model, reflections
|
619
|
+
hash_or_attr.to_s.pluralize.to_sym
|
620
|
+
end
|
621
|
+
|
584
622
|
obligations = attr_validator.engine.obligations(@privilege,
|
585
|
-
:context => @context
|
623
|
+
:context => @context,
|
586
624
|
:user => attr_validator.user)
|
625
|
+
|
587
626
|
obligations.collect {|obl| {hash_or_attr => obl} }
|
588
627
|
when Hash
|
589
628
|
obligations_array_attrs = []
|
590
629
|
obligations =
|
591
630
|
hash_or_attr.inject({}) do |all, pair|
|
592
631
|
attr, sub_hash = pair
|
593
|
-
all[attr] = obligation(attr_validator, sub_hash)
|
632
|
+
all[attr] = obligation(attr_validator, sub_hash, path + [attr])
|
594
633
|
if all[attr].length > 1
|
595
634
|
obligations_array_attrs << attr
|
596
635
|
else
|
@@ -622,6 +661,22 @@ module Authorization
|
|
622
661
|
def to_long_s
|
623
662
|
"if_permitted_to #{@privilege.inspect}, #{@attr_hash.inspect}"
|
624
663
|
end
|
664
|
+
|
665
|
+
private
|
666
|
+
def self.reflection_for_path (parent_model, path)
|
667
|
+
reflection = path.empty? ? parent_model : begin
|
668
|
+
parent = reflection_for_path(parent_model, path[0..-2])
|
669
|
+
if !parent.respond_to?(:proxy_reflection) and parent.respond_to?(:klass)
|
670
|
+
parent.klass.reflect_on_association(path.last)
|
671
|
+
else
|
672
|
+
parent.reflect_on_association(path.last)
|
673
|
+
end
|
674
|
+
rescue
|
675
|
+
parent.reflect_on_association(path.last)
|
676
|
+
end
|
677
|
+
raise "invalid path #{path.inspect}" if reflection.nil?
|
678
|
+
reflection
|
679
|
+
end
|
625
680
|
end
|
626
681
|
|
627
682
|
# Represents a pseudo-user to facilitate guest users in applications
|
@@ -12,6 +12,21 @@ module Authorization
|
|
12
12
|
|
13
13
|
DEFAULT_DENY = false
|
14
14
|
|
15
|
+
# If attribute_check is set for filter_access_to, decl_auth will try to
|
16
|
+
# load the appropriate object from the current controller's model with
|
17
|
+
# the id from params[:id]. If that fails, a 404 Not Found is often the
|
18
|
+
# right way to handle the error. If you have additional measures in place
|
19
|
+
# that restricts the find scope, handling this error as a permission denied
|
20
|
+
# might be a better way. Set failed_auto_loading_is_not_found to false
|
21
|
+
# for the latter behaviour.
|
22
|
+
@@failed_auto_loading_is_not_found = true
|
23
|
+
def self.failed_auto_loading_is_not_found?
|
24
|
+
@@failed_auto_loading_is_not_found
|
25
|
+
end
|
26
|
+
def self.failed_auto_loading_is_not_found= (new_value)
|
27
|
+
@@failed_auto_loading_is_not_found = new_value
|
28
|
+
end
|
29
|
+
|
15
30
|
# Returns the Authorization::Engine for the current controller.
|
16
31
|
def authorization_engine
|
17
32
|
@authorization_engine ||= Authorization::Engine.instance
|
@@ -36,7 +51,7 @@ module Authorization
|
|
36
51
|
def permitted_to! (privilege, object_or_sym = nil, options = {}, &block)
|
37
52
|
context = object = nil
|
38
53
|
if object_or_sym.nil?
|
39
|
-
context = self.class.
|
54
|
+
context = self.class.decl_auth_context
|
40
55
|
elsif object_or_sym.is_a?(Symbol)
|
41
56
|
context = object_or_sym
|
42
57
|
else
|
@@ -118,32 +133,32 @@ module Authorization
|
|
118
133
|
end
|
119
134
|
end
|
120
135
|
|
121
|
-
def load_controller_object (
|
122
|
-
instance_var = :"@#{
|
123
|
-
model =
|
136
|
+
def load_controller_object (context_without_namespace = nil) # :nodoc:
|
137
|
+
instance_var = :"@#{context_without_namespace.to_s.singularize}"
|
138
|
+
model = context_without_namespace.to_s.classify.constantize
|
124
139
|
instance_variable_set(instance_var, model.find(params[:id]))
|
125
140
|
end
|
126
141
|
|
127
|
-
def load_parent_controller_object (
|
128
|
-
instance_var = :"@#{
|
129
|
-
model =
|
130
|
-
instance_variable_set(instance_var, model.find(params[:"#{
|
142
|
+
def load_parent_controller_object (parent_context_without_namespace) # :nodoc:
|
143
|
+
instance_var = :"@#{parent_context_without_namespace.to_s.singularize}"
|
144
|
+
model = parent_context_without_namespace.to_s.classify.constantize
|
145
|
+
instance_variable_set(instance_var, model.find(params[:"#{parent_context_without_namespace.to_s.singularize}_id"]))
|
131
146
|
end
|
132
147
|
|
133
|
-
def new_controller_object_from_params (
|
134
|
-
model_or_proxy =
|
135
|
-
instance_variable_get(:"@#{
|
136
|
-
|
137
|
-
instance_var = :"@#{
|
148
|
+
def new_controller_object_from_params (context_without_namespace, parent_context_without_namespace) # :nodoc:
|
149
|
+
model_or_proxy = parent_context_without_namespace ?
|
150
|
+
instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
|
151
|
+
context_without_namespace.to_s.classify.constantize
|
152
|
+
instance_var = :"@#{context_without_namespace.to_s.singularize}"
|
138
153
|
instance_variable_set(instance_var,
|
139
|
-
model_or_proxy.new(params[
|
154
|
+
model_or_proxy.new(params[context_without_namespace.to_s.singularize]))
|
140
155
|
end
|
141
156
|
|
142
|
-
def new_controller_object_for_collection (
|
143
|
-
model_or_proxy =
|
144
|
-
instance_variable_get(:"@#{
|
145
|
-
|
146
|
-
instance_var = :"@#{
|
157
|
+
def new_controller_object_for_collection (context_without_namespace, parent_context_without_namespace) # :nodoc:
|
158
|
+
model_or_proxy = parent_context_without_namespace ?
|
159
|
+
instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
|
160
|
+
context_without_namespace.to_s.classify.constantize
|
161
|
+
instance_var = :"@#{context_without_namespace.to_s.singularize}"
|
147
162
|
instance_variable_set(instance_var, model_or_proxy.new)
|
148
163
|
end
|
149
164
|
|
@@ -221,7 +236,8 @@ module Authorization
|
|
221
236
|
# [:+require+]
|
222
237
|
# Privilege required; defaults to action_name
|
223
238
|
# [:+context+]
|
224
|
-
# The privilege's context, defaults to
|
239
|
+
# The privilege's context, defaults to decl_auth_context, which consists
|
240
|
+
# of controller_name, prepended by any namespaces
|
225
241
|
# [:+attribute_check+]
|
226
242
|
# Enables the check of attributes defined in the authorization rules.
|
227
243
|
# Defaults to false. If enabled, filter_access_to will use a context
|
@@ -485,6 +501,19 @@ module Authorization
|
|
485
501
|
end
|
486
502
|
end
|
487
503
|
end
|
504
|
+
|
505
|
+
# Returns the context for authorization checks in the current controller.
|
506
|
+
# Uses the controller_name and prepends any namespaces underscored and
|
507
|
+
# joined with underscores.
|
508
|
+
#
|
509
|
+
# E.g.
|
510
|
+
# AllThosePeopleController => :all_those_people
|
511
|
+
# AnyName::Space::ThingsController => :any_name_space_things
|
512
|
+
#
|
513
|
+
def decl_auth_context
|
514
|
+
prefixes = name.split('::')[0..-2].map(&:underscore)
|
515
|
+
((prefixes + [controller_name]) * '_').to_sym
|
516
|
+
end
|
488
517
|
|
489
518
|
protected
|
490
519
|
def filter_access_permissions # :nodoc:
|
@@ -545,22 +574,14 @@ module Authorization
|
|
545
574
|
if @filter_block
|
546
575
|
return contr.instance_eval(&@filter_block)
|
547
576
|
end
|
548
|
-
|
549
|
-
object = @attribute_check ? load_object(contr, context) : nil
|
577
|
+
object = @attribute_check ? load_object(contr) : nil
|
550
578
|
privilege = @privilege || :"#{contr.action_name}"
|
551
|
-
|
552
|
-
|
553
|
-
#puts " :user => #{contr.send(:current_user).inspect}, "
|
554
|
-
#puts " :object => #{object.inspect},"
|
555
|
-
#puts " :skip_attribute_test => #{!@attribute_check},"
|
556
|
-
#puts " :context => #{contr.class.controller_name.pluralize.to_sym})"
|
557
|
-
res = contr.authorization_engine.permit!(privilege,
|
579
|
+
|
580
|
+
contr.authorization_engine.permit!(privilege,
|
558
581
|
:user => contr.send(:current_user),
|
559
582
|
:object => object,
|
560
583
|
:skip_attribute_test => !@attribute_check,
|
561
|
-
:context => context)
|
562
|
-
#puts "permit? result: #{res.inspect}"
|
563
|
-
res
|
584
|
+
:context => @context || contr.class.decl_auth_context)
|
564
585
|
end
|
565
586
|
|
566
587
|
def remove_actions (actions)
|
@@ -569,24 +590,25 @@ module Authorization
|
|
569
590
|
end
|
570
591
|
|
571
592
|
private
|
572
|
-
def load_object(contr
|
593
|
+
def load_object(contr)
|
573
594
|
if @load_object_method and @load_object_method.is_a?(Symbol)
|
574
595
|
contr.send(@load_object_method)
|
575
596
|
elsif @load_object_method and @load_object_method.is_a?(Proc)
|
576
597
|
contr.instance_eval(&@load_object_method)
|
577
598
|
else
|
578
|
-
load_object_model = @load_object_model ||
|
599
|
+
load_object_model = @load_object_model ||
|
600
|
+
(@context ? @context.to_s.classify.constantize : contr.class.controller_name.classify.constantize)
|
579
601
|
instance_var = :"@#{load_object_model.name.underscore}"
|
580
602
|
object = contr.instance_variable_get(instance_var)
|
581
603
|
unless object
|
582
604
|
begin
|
583
605
|
object = load_object_model.find(contr.params[:id])
|
584
|
-
rescue
|
606
|
+
rescue RuntimeError => e
|
585
607
|
contr.logger.debug("filter_access_to tried to find " +
|
586
|
-
"#{load_object_model
|
608
|
+
"#{load_object_model} from params[:id] " +
|
587
609
|
"(#{contr.params[:id].inspect}), because attribute_check is enabled " +
|
588
|
-
"and #{instance_var.to_s} isn't set.")
|
589
|
-
raise
|
610
|
+
"and #{instance_var.to_s} isn't set, but failed: #{e.class.name}: #{e}")
|
611
|
+
raise if AuthorizationInController.failed_auto_loading_is_not_found?
|
590
612
|
end
|
591
613
|
contr.instance_variable_set(instance_var, object)
|
592
614
|
end
|
@@ -36,7 +36,11 @@ module Authorization
|
|
36
36
|
# Sets the current user for the declarative authorization plugin to the
|
37
37
|
# given one for the execution of the supplied block. Suitable for tests
|
38
38
|
# on certain users.
|
39
|
-
def with_user (user)
|
39
|
+
def with_user (user, &block)
|
40
|
+
Authorization::Maintenance.with_user(user, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.with_user (user)
|
40
44
|
prev_user = Authorization.current_user
|
41
45
|
Authorization.current_user = user
|
42
46
|
yield
|
@@ -138,7 +142,13 @@ module Authorization
|
|
138
142
|
with_user(user, &block)
|
139
143
|
end
|
140
144
|
end
|
141
|
-
|
145
|
+
|
146
|
+
# Test helper to test authorization rules. E.g.
|
147
|
+
# with_user a_normal_user do
|
148
|
+
# should_not_be_allowed_to :update, :conferences
|
149
|
+
# should_not_be_allowed_to :read, an_unpublished_conference
|
150
|
+
# should_be_allowed_to :read, a_published_conference
|
151
|
+
# end
|
142
152
|
def should_be_allowed_to (privilege, object_or_context)
|
143
153
|
options = {}
|
144
154
|
options[object_or_context.is_a?(Symbol) ? :context : :object] = object_or_context
|
@@ -147,6 +157,7 @@ module Authorization
|
|
147
157
|
end
|
148
158
|
end
|
149
159
|
|
160
|
+
# See should_be_allowed_to
|
150
161
|
def should_not_be_allowed_to (privilege, object_or_context)
|
151
162
|
options = {}
|
152
163
|
options[object_or_context.is_a?(Symbol) ? :context : :object] = object_or_context
|
@@ -46,6 +46,7 @@ module Authorization
|
|
46
46
|
# Consumes the given obligation, converting it into scope join and condition options.
|
47
47
|
def parse!( obligation )
|
48
48
|
@current_obligation = obligation
|
49
|
+
@join_table_joins = Set.new
|
49
50
|
obligation_conditions[@current_obligation] ||= {}
|
50
51
|
follow_path( obligation )
|
51
52
|
|
@@ -75,7 +76,7 @@ module Authorization
|
|
75
76
|
follow_comparison( steps, past_steps[0..-2], past_steps[-1] )
|
76
77
|
end
|
77
78
|
else
|
78
|
-
raise "invalid obligation path #{[past_steps, steps].
|
79
|
+
raise "invalid obligation path #{[past_steps, steps].inspect}"
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
@@ -120,7 +121,8 @@ module Authorization
|
|
120
121
|
end
|
121
122
|
|
122
123
|
# Returns the reflection corresponding to the given path.
|
123
|
-
def reflection_for(
|
124
|
+
def reflection_for(path, for_join_table_only = false)
|
125
|
+
@join_table_joins << path if for_join_table_only and !reflections[path]
|
124
126
|
reflections[path] ||= map_reflection_for( path )
|
125
127
|
end
|
126
128
|
|
@@ -147,6 +149,12 @@ module Authorization
|
|
147
149
|
|
148
150
|
reflections[path] = reflection
|
149
151
|
map_table_alias_for( path ) # Claim a table alias for the path.
|
152
|
+
|
153
|
+
# Claim alias for join table
|
154
|
+
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
155
|
+
join_table_path = path[0..-2] + [reflection.options[:through]]
|
156
|
+
reflection_for(join_table_path, true)
|
157
|
+
end
|
150
158
|
|
151
159
|
reflection
|
152
160
|
end
|
@@ -254,7 +262,7 @@ module Authorization
|
|
254
262
|
joins = (@proxy_options[:joins] || []) + (@proxy_options[:includes] || [])
|
255
263
|
|
256
264
|
reflections.keys.each do |path|
|
257
|
-
next if path.empty?
|
265
|
+
next if path.empty? or @join_table_joins.include?(path)
|
258
266
|
|
259
267
|
existing_join = joins.find do |join|
|
260
268
|
existing_path = join_to_path(join)
|
@@ -302,6 +302,18 @@ module Authorization
|
|
302
302
|
# if_attribute :branch => { :company => is {user.branch.company} }
|
303
303
|
# end
|
304
304
|
# end
|
305
|
+
#
|
306
|
+
# has_many and has_many through associations may also be nested.
|
307
|
+
# Then, at least one item in the association needs to fulfill the
|
308
|
+
# subsequent condition:
|
309
|
+
# if_attribute :company => { :branches => { :manager => { :last_name => is { user.last_name } } }
|
310
|
+
# Beware of possible performance issues when using has_many associations in
|
311
|
+
# permitted_to? checks. For
|
312
|
+
# permitted_to? :read, object
|
313
|
+
# a check like
|
314
|
+
# object.company.branches.any? { |branch| branch.manager ... }
|
315
|
+
# will be executed. with_permission_to scopes construct efficient SQL
|
316
|
+
# joins, though.
|
305
317
|
#
|
306
318
|
# Multiple attributes in one :if_attribute statement are AND'ed.
|
307
319
|
# Multiple if_attribute statements are OR'ed if the join operator for the
|
@@ -313,6 +325,8 @@ module Authorization
|
|
313
325
|
# if_attribute :branch => is {user.branch}, :changeable_by_coworker => true
|
314
326
|
# if_attribute :id => is {user.id}
|
315
327
|
# end
|
328
|
+
# The join operator for if_attribute rules can explicitly set to AND, though.
|
329
|
+
# See has_permission_on for details.
|
316
330
|
#
|
317
331
|
# Arrays and fixed values may be used directly as hash values:
|
318
332
|
# if_attribute :id => 1
|
@@ -343,6 +357,14 @@ module Authorization
|
|
343
357
|
# if_permitted_to associations may be nested as well:
|
344
358
|
# if_permitted_to :read, :branch => :company
|
345
359
|
#
|
360
|
+
# You can even use has_many associations as target. Then, it is checked
|
361
|
+
# if the current user has the required privilege on *any* of the target objects.
|
362
|
+
# if_permitted_to :read, :branch => :employees
|
363
|
+
# Beware of performance issues with permission checks. In the current implementation,
|
364
|
+
# all employees are checked until the first permitted is found.
|
365
|
+
# with_permissions_to, on the other hand, constructs more efficient SQL
|
366
|
+
# instead.
|
367
|
+
#
|
346
368
|
# To check permissions based on the current object, the attribute has to
|
347
369
|
# be left out:
|
348
370
|
# has_permission_on :branches, :to => :manage do
|
@@ -357,8 +379,9 @@ module Authorization
|
|
357
379
|
#
|
358
380
|
# Options:
|
359
381
|
# [:+context+]
|
360
|
-
#
|
361
|
-
#
|
382
|
+
# When using with_permissions_to, the target context of the if_permitted_to
|
383
|
+
# statement is infered from the last reflections target class. Still,
|
384
|
+
# you may override this algorithm by setting the context explicitly.
|
362
385
|
# if_permitted_to :read, :home_branch, :context => :branches
|
363
386
|
# if_permitted_to :read, :branch => :main_company, :context => :companies
|
364
387
|
#
|
data/test/authorization_test.rb
CHANGED
@@ -122,6 +122,23 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
122
122
|
engine.obligations(:test, :context => :permissions,
|
123
123
|
:user => MockUser.new(:test_role, :deeper_attr => 1, :deeper_attr_2 => 2))
|
124
124
|
end
|
125
|
+
|
126
|
+
def test_obligations_with_has_many
|
127
|
+
reader = Authorization::Reader::DSLReader.new
|
128
|
+
reader.parse %{
|
129
|
+
authorization do
|
130
|
+
role :test_role do
|
131
|
+
has_permission_on :permissions, :to => :test do
|
132
|
+
if_attribute :attrs => { :deeper_attr => is { user.deeper_attr } }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
}
|
137
|
+
engine = Authorization::Engine.new(reader)
|
138
|
+
assert_equal [{:attrs => {:deeper_attr => [:is, 1]}}],
|
139
|
+
engine.obligations(:test, :context => :permissions,
|
140
|
+
:user => MockUser.new(:test_role, :deeper_attr => 1))
|
141
|
+
end
|
125
142
|
|
126
143
|
def test_obligations_with_conditions_and_empty
|
127
144
|
reader = Authorization::Reader::DSLReader.new
|
@@ -174,6 +191,39 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
174
191
|
:user => MockUser.new(:test_role, :attr => 1))
|
175
192
|
end
|
176
193
|
|
194
|
+
def test_obligations_with_has_many_permissions
|
195
|
+
reader = Authorization::Reader::DSLReader.new
|
196
|
+
reader.parse %{
|
197
|
+
authorization do
|
198
|
+
role :test_role do
|
199
|
+
has_permission_on :permissions, :to => :test do
|
200
|
+
if_attribute :attr => is { user.attr }
|
201
|
+
end
|
202
|
+
has_permission_on :permission_children, :to => :test do
|
203
|
+
if_permitted_to :test, :permissions, :context => :permissions
|
204
|
+
end
|
205
|
+
has_permission_on :permission_children_2, :to => :test do
|
206
|
+
if_permitted_to :test, :permissions
|
207
|
+
end
|
208
|
+
has_permission_on :permission_children_children, :to => :test do
|
209
|
+
if_permitted_to :test, :permission_child => :permissions,
|
210
|
+
:context => :permissions
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
}
|
215
|
+
engine = Authorization::Engine.new(reader)
|
216
|
+
assert_equal [{:permissions => {:attr => [:is, 1]}}],
|
217
|
+
engine.obligations(:test, :context => :permission_children,
|
218
|
+
:user => MockUser.new(:test_role, :attr => 1))
|
219
|
+
assert_equal [{:permissions => {:attr => [:is, 1]}}],
|
220
|
+
engine.obligations(:test, :context => :permission_children_2,
|
221
|
+
:user => MockUser.new(:test_role, :attr => 1))
|
222
|
+
assert_equal [{:permission_child => {:permissions => {:attr => [:is, 1]}}}],
|
223
|
+
engine.obligations(:test, :context => :permission_children_children,
|
224
|
+
:user => MockUser.new(:test_role, :attr => 1))
|
225
|
+
end
|
226
|
+
|
177
227
|
def test_obligations_with_permissions_multiple
|
178
228
|
reader = Authorization::Reader::DSLReader.new
|
179
229
|
reader.parse %{
|
@@ -501,17 +551,43 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
501
551
|
end
|
502
552
|
|
|
503
553
|
engine = Authorization::Engine.new(reader)
|
504
|
-
attr_1_struct = Struct.new(:test_attr_2)
|
505
554
|
assert engine.permit?(:test, :context => :permissions,
|
506
555
|
:user => MockUser.new(:test_role),
|
507
|
-
:object => MockDataObject.new(:test_attr_1 =>
|
556
|
+
:object => MockDataObject.new(:test_attr_1 =>
|
557
|
+
MockDataObject.new(:test_attr_2 => [1,2])))
|
508
558
|
assert !engine.permit?(:test, :context => :permissions,
|
509
559
|
:user => MockUser.new(:test_role),
|
510
|
-
:object => MockDataObject.new(:test_attr_1 =>
|
560
|
+
:object => MockDataObject.new(:test_attr_1 =>
|
561
|
+
MockDataObject.new(:test_attr_2 => [3,4])))
|
511
562
|
assert_equal [{:test_attr_1 => {:test_attr_2 => [:contains, 1]}}],
|
512
563
|
engine.obligations(:test, :context => :permissions,
|
513
564
|
:user => MockUser.new(:test_role))
|
514
565
|
end
|
566
|
+
|
567
|
+
def test_attribute_has_many
|
568
|
+
reader = Authorization::Reader::DSLReader.new
|
569
|
+
reader.parse %|
|
570
|
+
authorization do
|
571
|
+
role :test_role do
|
572
|
+
has_permission_on :companies, :to => :read do
|
573
|
+
if_attribute :branches => {:city => is { user.city } }
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
|
578
|
+
engine = Authorization::Engine.new(reader)
|
579
|
+
|
580
|
+
company = MockDataObject.new(:branches => [
|
581
|
+
MockDataObject.new(:city => 'Barcelona'),
|
582
|
+
MockDataObject.new(:city => 'Paris')
|
583
|
+
])
|
584
|
+
assert engine.permit!(:read, :context => :companies,
|
585
|
+
:user => MockUser.new(:test_role, :city => 'Paris'),
|
586
|
+
:object => company)
|
587
|
+
assert !engine.permit?(:read, :context => :companies,
|
588
|
+
:user => MockUser.new(:test_role, :city => 'London'),
|
589
|
+
:object => company)
|
590
|
+
end
|
515
591
|
|
516
592
|
def test_attribute_non_block
|
517
593
|
reader = Authorization::Reader::DSLReader.new
|
@@ -585,6 +661,32 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
585
661
|
:object => MockDataObject.new(:permission => perm_data_attr_2))
|
586
662
|
end
|
587
663
|
|
664
|
+
def test_attribute_with_has_many_permissions
|
665
|
+
reader = Authorization::Reader::DSLReader.new
|
666
|
+
reader.parse %{
|
667
|
+
authorization do
|
668
|
+
role :test_role do
|
669
|
+
has_permission_on :permissions, :to => :test do
|
670
|
+
if_attribute :test_attr => 1
|
671
|
+
end
|
672
|
+
has_permission_on :permission_children, :to => :test do
|
673
|
+
if_permitted_to :test, :permissions
|
674
|
+
end
|
675
|
+
end
|
676
|
+
end
|
677
|
+
}
|
678
|
+
engine = Authorization::Engine.new(reader)
|
679
|
+
|
680
|
+
perm_data_attr_1 = PermissionMock.new({:test_attr => 1})
|
681
|
+
perm_data_attr_2 = PermissionMock.new({:test_attr => 2})
|
682
|
+
assert engine.permit?(:test, :context => :permission_children,
|
683
|
+
:user => MockUser.new(:test_role),
|
684
|
+
:object => MockDataObject.new(:permissions => [perm_data_attr_1]))
|
685
|
+
assert !engine.permit?(:test, :context => :permission_children,
|
686
|
+
:user => MockUser.new(:test_role),
|
687
|
+
:object => MockDataObject.new(:permissions => [perm_data_attr_2]))
|
688
|
+
end
|
689
|
+
|
588
690
|
def test_attribute_with_deep_permissions
|
589
691
|
reader = Authorization::Reader::DSLReader.new
|
590
692
|
reader.parse %{
|
@@ -613,6 +715,34 @@ class AuthorizationTest < Test::Unit::TestCase
|
|
613
715
|
MockDataObject.new(:permission => perm_data_attr_2)))
|
614
716
|
end
|
615
717
|
|
718
|
+
def test_attribute_with_deep_has_many_permissions
|
719
|
+
reader = Authorization::Reader::DSLReader.new
|
720
|
+
reader.parse %{
|
721
|
+
authorization do
|
722
|
+
role :test_role do
|
723
|
+
has_permission_on :permissions, :to => :test do
|
724
|
+
if_attribute :test_attr => 1
|
725
|
+
end
|
726
|
+
has_permission_on :permission_children, :to => :test do
|
727
|
+
if_permitted_to :test, :shallow_permissions => :permission
|
728
|
+
end
|
729
|
+
end
|
730
|
+
end
|
731
|
+
}
|
732
|
+
engine = Authorization::Engine.new(reader)
|
733
|
+
|
734
|
+
perm_data_attr_1 = PermissionMock.new({:test_attr => 1})
|
735
|
+
perm_data_attr_2 = PermissionMock.new({:test_attr => 2})
|
736
|
+
assert engine.permit?(:test, :context => :permission_children,
|
737
|
+
:user => MockUser.new(:test_role),
|
738
|
+
:object => MockDataObject.new(:shallow_permissions =>
|
739
|
+
[MockDataObject.new(:permission => perm_data_attr_1)]))
|
740
|
+
assert !engine.permit?(:test, :context => :permission_children,
|
741
|
+
:user => MockUser.new(:test_role),
|
742
|
+
:object => MockDataObject.new(:shallow_permissions =>
|
743
|
+
[MockDataObject.new(:permission => perm_data_attr_2)]))
|
744
|
+
end
|
745
|
+
|
616
746
|
def test_attribute_with_permissions_nil
|
617
747
|
reader = Authorization::Reader::DSLReader.new
|
618
748
|
reader.parse %{
|
data/test/controller_test.rb
CHANGED
@@ -254,6 +254,13 @@ class LoadObjectControllerTest < ActionController::TestCase
|
|
254
254
|
assert_raise RuntimeError, "No id param supplied" do
|
255
255
|
request!(MockUser.new(:test_role), "show", reader)
|
256
256
|
end
|
257
|
+
|
258
|
+
Authorization::AuthorizationInController.failed_auto_loading_is_not_found = false
|
259
|
+
assert_nothing_raised "Load error is only logged" do
|
260
|
+
request!(MockUser.new(:test_role), "show", reader)
|
261
|
+
end
|
262
|
+
assert !@controller.authorized?
|
263
|
+
Authorization::AuthorizationInController.failed_auto_loading_is_not_found = true
|
257
264
|
end
|
258
265
|
|
259
266
|
def test_filter_access_with_object_load_custom
|
@@ -384,3 +391,74 @@ class HierachicalControllerTest < ActionController::TestCase
|
|
384
391
|
assert !@controller.authorized?
|
385
392
|
end
|
386
393
|
end
|
394
|
+
|
395
|
+
##################
|
396
|
+
module Name
|
397
|
+
class SpacedThingsController < MocksController
|
398
|
+
filter_access_to :show
|
399
|
+
filter_access_to :update, :context => :spaced_things
|
400
|
+
define_action_methods :show, :update
|
401
|
+
end
|
402
|
+
end
|
403
|
+
class NameSpacedControllerTest < ActionController::TestCase
|
404
|
+
tests Name::SpacedThingsController
|
405
|
+
def test_context
|
406
|
+
reader = Authorization::Reader::DSLReader.new
|
407
|
+
reader.parse %{
|
408
|
+
authorization do
|
409
|
+
role :permitted_role do
|
410
|
+
has_permission_on :name_spaced_things, :to => :show
|
411
|
+
has_permission_on :spaced_things, :to => :update
|
412
|
+
end
|
413
|
+
role :prohibited_role do
|
414
|
+
has_permission_on :name_spaced_things, :to => :update
|
415
|
+
has_permission_on :spaced_things, :to => :show
|
416
|
+
end
|
417
|
+
end
|
418
|
+
}
|
419
|
+
request!(MockUser.new(:permitted_role), "show", reader)
|
420
|
+
assert @controller.authorized?
|
421
|
+
request!(MockUser.new(:prohibited_role), "show", reader)
|
422
|
+
assert !@controller.authorized?
|
423
|
+
request!(MockUser.new(:permitted_role), "update", reader)
|
424
|
+
assert @controller.authorized?
|
425
|
+
request!(MockUser.new(:prohibited_role), "update", reader)
|
426
|
+
assert !@controller.authorized?
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
module Deep
|
431
|
+
module NameSpaced
|
432
|
+
class ThingsController < MocksController
|
433
|
+
filter_access_to :show
|
434
|
+
filter_access_to :update, :context => :things
|
435
|
+
define_action_methods :show, :update
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
class DeepNameSpacedControllerTest < ActionController::TestCase
|
440
|
+
tests Deep::NameSpaced::ThingsController
|
441
|
+
def test_context
|
442
|
+
reader = Authorization::Reader::DSLReader.new
|
443
|
+
reader.parse %{
|
444
|
+
authorization do
|
445
|
+
role :permitted_role do
|
446
|
+
has_permission_on :deep_name_spaced_things, :to => :show
|
447
|
+
has_permission_on :things, :to => :update
|
448
|
+
end
|
449
|
+
role :prohibited_role do
|
450
|
+
has_permission_on :deep_name_spaced_things, :to => :update
|
451
|
+
has_permission_on :things, :to => :show
|
452
|
+
end
|
453
|
+
end
|
454
|
+
}
|
455
|
+
request!(MockUser.new(:permitted_role), "show", reader)
|
456
|
+
assert @controller.authorized?
|
457
|
+
request!(MockUser.new(:prohibited_role), "show", reader)
|
458
|
+
assert !@controller.authorized?
|
459
|
+
request!(MockUser.new(:permitted_role), "update", reader)
|
460
|
+
assert @controller.authorized?
|
461
|
+
request!(MockUser.new(:prohibited_role), "update", reader)
|
462
|
+
assert !@controller.authorized?
|
463
|
+
end
|
464
|
+
end
|
data/test/model_test.rb
CHANGED
@@ -15,6 +15,7 @@ end
|
|
15
15
|
|
16
16
|
class TestModel < ActiveRecord::Base
|
17
17
|
has_many :test_attrs
|
18
|
+
has_many :test_another_attrs, :class_name => "TestAttr", :foreign_key => :test_another_model_id
|
18
19
|
has_many :test_attr_throughs, :through => :test_attrs
|
19
20
|
has_many :test_attrs_with_attr, :class_name => "TestAttr", :conditions => {:attr => 1}
|
20
21
|
has_many :test_attr_throughs_with_attr, :through => :test_attrs,
|
@@ -148,6 +149,66 @@ class ModelTest < Test::Unit::TestCase
|
|
148
149
|
TestAttr.delete_all
|
149
150
|
TestModel.delete_all
|
150
151
|
end
|
152
|
+
|
153
|
+
def test_named_scope_with_nested_has_many
|
154
|
+
reader = Authorization::Reader::DSLReader.new
|
155
|
+
reader.parse %{
|
156
|
+
authorization do
|
157
|
+
role :test_role do
|
158
|
+
has_permission_on :companies, :to => :read do
|
159
|
+
if_attribute :branches => { :test_attrs => { :attr => is { user.test_attr_value } } }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
}
|
164
|
+
Authorization::Engine.instance(reader)
|
165
|
+
|
166
|
+
allowed_company = Company.create!
|
167
|
+
allowed_company.branches.create!.test_attrs.create!(:attr => 1)
|
168
|
+
allowed_company.branches.create!.test_attrs.create!(:attr => 2)
|
169
|
+
|
170
|
+
prohibited_company = Company.create!
|
171
|
+
prohibited_company.branches.create!.test_attrs.create!(:attr => 3)
|
172
|
+
|
173
|
+
user = MockUser.new(:test_role, :test_attr_value => 1)
|
174
|
+
prohibited_user = MockUser.new(:test_role, :test_attr_value => 4)
|
175
|
+
assert_equal 1, Company.with_permissions_to(:read, :user => user).length
|
176
|
+
assert_equal 0, Company.with_permissions_to(:read, :user => prohibited_user).length
|
177
|
+
|
178
|
+
Company.delete_all
|
179
|
+
Branch.delete_all
|
180
|
+
TestAttr.delete_all
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_named_scope_with_nested_has_many_through
|
184
|
+
reader = Authorization::Reader::DSLReader.new
|
185
|
+
reader.parse %{
|
186
|
+
authorization do
|
187
|
+
role :test_role do
|
188
|
+
has_permission_on :test_models, :to => :read do
|
189
|
+
if_attribute :test_attr_throughs => { :test_attr => { :attr => is { user.test_attr_value } } }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
}
|
194
|
+
Authorization::Engine.instance(reader)
|
195
|
+
|
196
|
+
allowed_model = TestModel.create!
|
197
|
+
allowed_model.test_attrs.create!(:attr => 1).test_attr_throughs.create!
|
198
|
+
allowed_model.test_attrs.create!(:attr => 2).test_attr_throughs.create!
|
199
|
+
|
200
|
+
prohibited_model = TestModel.create!
|
201
|
+
prohibited_model.test_attrs.create!(:attr => 3).test_attr_throughs.create!
|
202
|
+
|
203
|
+
user = MockUser.new(:test_role, :test_attr_value => 1)
|
204
|
+
prohibited_user = MockUser.new(:test_role, :test_attr_value => 4)
|
205
|
+
assert_equal 1, TestModel.with_permissions_to(:read, :user => user).length
|
206
|
+
assert_equal 0, TestModel.with_permissions_to(:read, :user => prohibited_user).length
|
207
|
+
|
208
|
+
TestModel.delete_all
|
209
|
+
TestAttrThrough.delete_all
|
210
|
+
TestAttr.delete_all
|
211
|
+
end
|
151
212
|
|
152
213
|
def test_named_scope_with_is
|
153
214
|
reader = Authorization::Reader::DSLReader.new
|
@@ -831,6 +892,7 @@ class ModelTest < Test::Unit::TestCase
|
|
831
892
|
|
832
893
|
TestModel.delete_all
|
833
894
|
TestAttr.delete_all
|
895
|
+
TestAttrThrough.delete_all
|
834
896
|
end
|
835
897
|
|
836
898
|
def test_named_scope_with_is_in
|
@@ -912,6 +974,131 @@ class ModelTest < Test::Unit::TestCase
|
|
912
974
|
TestAttr.delete_all
|
913
975
|
end
|
914
976
|
|
977
|
+
def test_named_scope_with_if_permitted_to_with_no_child_permissions
|
978
|
+
reader = Authorization::Reader::DSLReader.new
|
979
|
+
reader.parse %{
|
980
|
+
authorization do
|
981
|
+
role :another_role do
|
982
|
+
has_permission_on :test_models, :to => :read do
|
983
|
+
if_attribute :test_attrs => contains { user }
|
984
|
+
end
|
985
|
+
end
|
986
|
+
role :additional_if_attribute do
|
987
|
+
has_permission_on :test_attrs, :to => :read do
|
988
|
+
if_permitted_to :read, :test_model
|
989
|
+
if_attribute :test_model => {:test_attrs => contains { user }}
|
990
|
+
end
|
991
|
+
end
|
992
|
+
role :only_permitted_to do
|
993
|
+
has_permission_on :test_attrs, :to => :read do
|
994
|
+
if_permitted_to :read, :test_model
|
995
|
+
end
|
996
|
+
end
|
997
|
+
end
|
998
|
+
}
|
999
|
+
Authorization::Engine.instance(reader)
|
1000
|
+
|
1001
|
+
test_model_1 = TestModel.create!
|
1002
|
+
test_attr_1 = test_model_1.test_attrs.create!
|
1003
|
+
|
1004
|
+
user = MockUser.new(:only_permitted_to, :another_role, :id => test_attr_1.id)
|
1005
|
+
also_allowed_user = MockUser.new(:additional_if_attribute, :id => test_attr_1.id)
|
1006
|
+
non_allowed_user = MockUser.new(:only_permitted_to, :id => test_attr_1.id)
|
1007
|
+
|
1008
|
+
assert_equal 1, TestAttr.with_permissions_to(:read, :user => user).length
|
1009
|
+
assert_equal 1, TestAttr.with_permissions_to(:read, :user => also_allowed_user).length
|
1010
|
+
assert_raise Authorization::NotAuthorized do
|
1011
|
+
TestAttr.with_permissions_to(:read, :user => non_allowed_user).find(:all)
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
TestModel.delete_all
|
1015
|
+
TestAttr.delete_all
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
def test_named_scope_with_if_permitted_to_with_context_from_model
|
1019
|
+
reader = Authorization::Reader::DSLReader.new
|
1020
|
+
reader.parse %{
|
1021
|
+
authorization do
|
1022
|
+
role :test_role do
|
1023
|
+
has_permission_on :test_models, :to => :read do
|
1024
|
+
if_attribute :test_another_attrs => contains { user }
|
1025
|
+
end
|
1026
|
+
has_permission_on :test_attrs, :to => :read do
|
1027
|
+
if_permitted_to :read, :test_another_model
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
}
|
1032
|
+
Authorization::Engine.instance(reader)
|
1033
|
+
|
1034
|
+
test_model_1 = TestModel.create!
|
1035
|
+
test_attr_1 = test_model_1.test_another_attrs.create!
|
1036
|
+
|
1037
|
+
user = MockUser.new(:test_role, :id => test_attr_1.id)
|
1038
|
+
non_allowed_user = MockUser.new(:test_role, :id => 111)
|
1039
|
+
|
1040
|
+
assert_equal 1, TestAttr.with_permissions_to(:read, :user => user).length
|
1041
|
+
assert_equal 0, TestAttr.with_permissions_to(:read, :user => non_allowed_user).length
|
1042
|
+
TestModel.delete_all
|
1043
|
+
TestAttr.delete_all
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
def test_named_scope_with_has_many_if_permitted_to
|
1047
|
+
reader = Authorization::Reader::DSLReader.new
|
1048
|
+
reader.parse %{
|
1049
|
+
authorization do
|
1050
|
+
role :test_role do
|
1051
|
+
has_permission_on :test_models, :to => :read do
|
1052
|
+
if_permitted_to :read, :test_attrs
|
1053
|
+
end
|
1054
|
+
has_permission_on :test_attrs, :to => :read do
|
1055
|
+
if_attribute :attr => is { user.id }
|
1056
|
+
end
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
}
|
1060
|
+
Authorization::Engine.instance(reader)
|
1061
|
+
|
1062
|
+
test_model_1 = TestModel.create!
|
1063
|
+
test_attr_1 = test_model_1.test_attrs.create!(:attr => 111)
|
1064
|
+
|
1065
|
+
user = MockUser.new(:test_role, :id => test_attr_1.attr)
|
1066
|
+
non_allowed_user = MockUser.new(:test_role, :id => 333)
|
1067
|
+
assert_equal 1, TestModel.with_permissions_to(:read, :user => user).length
|
1068
|
+
assert_equal 0, TestModel.with_permissions_to(:read, :user => non_allowed_user).length
|
1069
|
+
TestModel.delete_all
|
1070
|
+
TestAttr.delete_all
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
def test_named_scope_with_deep_has_many_if_permitted_to
|
1074
|
+
reader = Authorization::Reader::DSLReader.new
|
1075
|
+
reader.parse %{
|
1076
|
+
authorization do
|
1077
|
+
role :test_role do
|
1078
|
+
has_permission_on :branches, :to => :read do
|
1079
|
+
if_attribute :name => "A Branch"
|
1080
|
+
end
|
1081
|
+
has_permission_on :companies, :to => :read do
|
1082
|
+
if_permitted_to :read, :test_attrs => :branch
|
1083
|
+
end
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
}
|
1087
|
+
Authorization::Engine.instance(reader)
|
1088
|
+
|
1089
|
+
readable_company = Company.create!
|
1090
|
+
readable_company.test_attrs.create!(:branch => Branch.create!(:name => "A Branch"))
|
1091
|
+
|
1092
|
+
forbidden_company = Company.create!
|
1093
|
+
forbidden_company.test_attrs.create!(:branch => Branch.create!(:name => "Different Branch"))
|
1094
|
+
|
1095
|
+
user = MockUser.new(:test_role)
|
1096
|
+
assert_equal 1, Company.with_permissions_to(:read, :user => user).length
|
1097
|
+
Company.delete_all
|
1098
|
+
Branch.delete_all
|
1099
|
+
TestAttr.delete_all
|
1100
|
+
end
|
1101
|
+
|
915
1102
|
def test_named_scope_with_if_permitted_to_and_empty_obligations
|
916
1103
|
reader = Authorization::Reader::DSLReader.new
|
917
1104
|
reader.parse %{
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: declarative_authorization
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: "0.4"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steffen Bartsch
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-15 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|