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 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].to_sym.inspect} #{h step[2].to_sym.inspect}</strong> to any role",
94
- "Don't suggest adding #{h step[1].to_sym.inspect} #{h step[2].to_sym.inspect}.", options)
95
- "Add privilege <strong>#{h step[1].inspect} #{h step[2].inspect}</strong>#{dont_assign} to role <strong>#{h step[3].to_sym.inspect}</strong>"
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].to_sym.inspect} #{h step[2].to_sym.inspect}</strong> from any role",
99
- "Don't suggest removing #{h step[1].to_sym.inspect} #{h step[2].to_sym.inspect}.", options)
100
- "Remove privilege <strong>#{h step[1].inspect} #{h step[2].inspect}</strong>#{dont_remove} from role <strong>#{h step[3].to_sym.inspect}</strong>"
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.inspect}</strong>"
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.inspect}</strong> to any user",
106
- "Don't suggest assigning #{h step[1].to_sym.inspect}.", options)
107
- "Assign role <strong>#{h step[1].to_sym.inspect}</strong>#{dont_assign} to <strong>#{h readable_step_info(step[2])}</strong>"
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.inspect}</strong> from any user",
111
- "Don't suggest removing #{h step[1].to_sym.inspect}.", options)
112
- "Remove role <strong>#{h step[1].to_sym.inspect}</strong>#{dont_remove} from <strong>#{h readable_step_info(step[2])}</strong>"
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(&:to_s).sort, @privilege.to_s) %>
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(&:to_s).sort, @context.to_s) %>
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 #{pluralize grouped_approach.similar_approaches.length, "suggestion"} in this group", "show_group_approaches(#{group_index})", :class => "show-others-in-group" %>
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 #{pluralize(@grouped_approaches.length - 3, 'group')}", "show_all_groups(); $(this).up().hide()" %></li>
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
- if (!refresh)
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.inspect %>"
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.inspect %>"<%= ',fontcolor="#ff0000"' if @highlight_privilege == 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
- obligations = @attributes.collect {|attr| attr.obligation(attr_validator) }.flatten
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
- if attr_value.is_a?(Array)
415
- raise AuthorizationUsageError, "Unable evaluate multiple attributes " +
416
- "on a collection. Cannot use '=>' operator on #{attr.inspect} " +
417
- "(#{attr_value.inspect}) for attributes #{value.inspect}."
418
- elsif attr_value.nil?
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
- validate?(attr_validator, attr_value, value)
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
- if attr_value.nil?
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
- if attr_value.nil?
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 || hash_or_attr.to_s.pluralize.to_sym,
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.controller_name.to_sym
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 (context) # :nodoc:
122
- instance_var = :"@#{context.to_s.singularize}"
123
- model = context.to_s.classify.constantize
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 (parent_context) # :nodoc:
128
- instance_var = :"@#{parent_context.to_s.singularize}"
129
- model = parent_context.to_s.classify.constantize
130
- instance_variable_set(instance_var, model.find(params[:"#{parent_context.to_s.singularize}_id"]))
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 (context, parent_context) # :nodoc:
134
- model_or_proxy = parent_context ?
135
- instance_variable_get(:"@#{parent_context.to_s.singularize}").send(context.to_sym) :
136
- context.to_s.classify.constantize
137
- instance_var = :"@#{context.to_s.singularize}"
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[context.to_s.singularize]))
154
+ model_or_proxy.new(params[context_without_namespace.to_s.singularize]))
140
155
  end
141
156
 
142
- def new_controller_object_for_collection (context, parent_context) # :nodoc:
143
- model_or_proxy = parent_context ?
144
- instance_variable_get(:"@#{parent_context.to_s.singularize}").send(context.to_sym) :
145
- context.to_s.classify.constantize
146
- instance_var = :"@#{context.to_s.singularize}"
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 controller_name, pluralized.
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
- context = @context || contr.class.controller_name.to_sym
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
- #puts "Trying permit?(#{privilege.inspect}, "
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, context)
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 || context.to_s.classify.constantize
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 ActiveRecord::RecordNotFound, RuntimeError
606
+ rescue RuntimeError => e
585
607
  contr.logger.debug("filter_access_to tried to find " +
586
- "#{load_object_model.inspect} from params[:id] " +
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].flatten}"
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( path )
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
- # If the context of the refered object may not be infered from the
361
- # associations name, the context may be given explicitly:
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
  #
@@ -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 => attr_1_struct.new([1,2])))
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 => attr_1_struct.new([3,4])))
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 %{
@@ -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.3.2.3
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-10-12 00:00:00 +02:00
12
+ date: 2009-11-15 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency