declarative_authorization 0.3.2.3 → 0.4
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/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
|