declarative_authorization 0.3.2.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG 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