uhees-declarative_authorization 0.3.1 → 0.3.2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README.rdoc +24 -7
- data/app/controllers/authorization_rules_controller.rb +29 -5
- data/app/helpers/authorization_rules_helper.rb +7 -3
- data/app/views/authorization_rules/_change.erb +12 -3
- data/app/views/authorization_rules/_show_graph.erb +2 -2
- data/app/views/authorization_rules/_suggestions.erb +38 -14
- data/app/views/authorization_rules/change.html.erb +37 -9
- data/lib/declarative_authorization/authorization.rb +10 -7
- data/lib/declarative_authorization/development_support/analyzer.rb +2 -2
- data/lib/declarative_authorization/development_support/change_supporter.rb +46 -4
- data/lib/declarative_authorization/in_controller.rb +255 -3
- data/lib/declarative_authorization/in_model.rb +11 -2
- data/lib/declarative_authorization/maintenance.rb +2 -8
- data/lib/declarative_authorization/obligation_scope.rb +18 -7
- data/lib/declarative_authorization/reader.rb +4 -1
- data/test/authorization_test.rb +6 -2
- data/test/controller_filter_resource_access_test.rb +394 -0
- data/test/controller_test.rb +19 -8
- data/test/maintenance_test.rb +5 -0
- data/test/model_test.rb +137 -0
- data/test/schema.sql +1 -0
- data/test/test_helper.rb +18 -3
- metadata +5 -4
- data/app/views/authorization_rules/_suggestion.erb +0 -9
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
* Change Support: Suggestion grouping, sort by affected users [sb]
|
2
|
+
|
3
|
+
* Changed context derived from objects to #class.name.tableize to fix STI [sb]
|
4
|
+
|
5
|
+
* Simplified controller authorization with filter_resource_access [sb]
|
6
|
+
|
1
7
|
* Allow passing explicit context in addition to object in permitted_to? [Olly Lylo, sb]
|
2
8
|
|
3
9
|
* Change Supporter: suggest changes to authorization rules [sb]
|
data/README.rdoc
CHANGED
@@ -79,12 +79,26 @@ generated yourself or at http://www.tzi.org/~sbartsch/declarative_authorization
|
|
79
79
|
|
80
80
|
== Controller
|
81
81
|
|
82
|
-
If authentication is in place,
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
If authentication is in place, there are two ways to enable user-specific
|
83
|
+
access control on controller actions. For resource controllers, which more
|
84
|
+
or less follow the CRUD pattern, +filter_resource_access+ is the simplest
|
85
|
+
approach. It sets up instance variables in before filters and calls
|
86
|
+
filter_access_to with the appropriate parameters to protect the CRUD methods.
|
87
|
+
|
88
|
+
class EmployeesController < ApplicationController
|
89
|
+
filter_resource_access
|
90
|
+
...
|
91
|
+
end
|
92
|
+
|
93
|
+
See Authorization::AuthorizationInController::ClassMethods for options on
|
94
|
+
nested resources and custom member and collection actions.
|
95
|
+
|
96
|
+
If you prefer less magic or your controller has no resemblance with the resource
|
97
|
+
controllers, directly calling filter_access_to may be the better option. Examples
|
98
|
+
are given in the following. E.g. the privilege index users is required for
|
99
|
+
action index. This works as a first default configuration for RESTful
|
100
|
+
controllers, with these privileges easily handled in the authorization
|
101
|
+
configuration, which will be described below.
|
88
102
|
|
89
103
|
class EmployeesController < ApplicationController
|
90
104
|
filter_access_to :all
|
@@ -456,7 +470,7 @@ and Ubuntu) and has only been tested under Linux.
|
|
456
470
|
|
457
471
|
= Help and Contact
|
458
472
|
|
459
|
-
We have an issue tracker[http://
|
473
|
+
We have an issue tracker[http://github.com/stffn/declarative_authorization/issues]
|
460
474
|
for bugs and feature requests as well as a
|
461
475
|
Google Group[http://groups.google.com/group/declarative_authorization] for
|
462
476
|
discussions on the usage of the plugin. You are very welcome to contribute.
|
@@ -475,11 +489,14 @@ sbartsch at tzi.org
|
|
475
489
|
Thanks to
|
476
490
|
* Eike Carls
|
477
491
|
* Erik Dahlstrand
|
492
|
+
* Jeroen van Dijk
|
478
493
|
* Jeremy Friesen
|
479
494
|
* Brian Langenfeld
|
495
|
+
* Georg Ledermann
|
480
496
|
* Geoff Longman
|
481
497
|
* Olly Lylo
|
482
498
|
* Mark Mansour
|
499
|
+
* Thomas Maurer
|
483
500
|
* Mike Vincent
|
484
501
|
|
485
502
|
|
@@ -35,7 +35,10 @@ class AuthorizationRulesController < ApplicationController
|
|
35
35
|
@users.sort! {|a, b| a.login <=> b.login }
|
36
36
|
|
37
37
|
@privileges = authorization_engine.auth_rules.collect {|rule| rule.privileges.to_a}.flatten.uniq
|
38
|
-
@privileges = @privileges.collect
|
38
|
+
@privileges = @privileges.collect do |priv|
|
39
|
+
priv = Authorization::DevelopmentSupport::AnalyzerEngine::Privilege.for_sym(priv, authorization_engine)
|
40
|
+
(priv.descendants + priv.ancestors).map(&:to_sym)
|
41
|
+
end.flatten.uniq
|
39
42
|
@privileges.sort_by {|priv| priv.to_s}
|
40
43
|
@privilege = params[:privilege].to_sym rescue @privileges.first
|
41
44
|
@contexts = authorization_engine.auth_rules.collect {|rule| rule.contexts.to_a}.flatten.uniq
|
@@ -64,19 +67,40 @@ class AuthorizationRulesController < ApplicationController
|
|
64
67
|
deserialize_changes(spec).flatten
|
65
68
|
end
|
66
69
|
|
67
|
-
users_keys = users_permission.keys
|
68
70
|
analyzer = Authorization::DevelopmentSupport::ChangeSupporter.new(authorization_engine)
|
69
71
|
|
70
72
|
privilege = params[:privilege].to_sym
|
71
73
|
context = params[:context].to_sym
|
74
|
+
all_users = User.all
|
72
75
|
@context = context
|
73
|
-
@approaches = analyzer.find_approaches_for(:users =>
|
76
|
+
@approaches = analyzer.find_approaches_for(:users => all_users, :prohibited_actions => prohibited_actions) do
|
74
77
|
users.each_with_index do |user, idx|
|
75
|
-
|
76
|
-
|
78
|
+
unless users_permission[all_users[idx]].nil?
|
79
|
+
args = [privilege, {:context => context, :user => user}]
|
80
|
+
assert(users_permission[all_users[idx]] ? permit?(*args) : !permit?(*args))
|
81
|
+
end
|
77
82
|
end
|
78
83
|
end
|
79
84
|
|
85
|
+
@affected_users = @approaches.each_with_object({}) do |approach, memo|
|
86
|
+
memo[approach] = approach.affected_users(authorization_engine, all_users, privilege, context).length
|
87
|
+
end
|
88
|
+
max_affected_users = @affected_users.values.max
|
89
|
+
if params[:affected_users]
|
90
|
+
@approaches = @approaches.sort_by do |approach|
|
91
|
+
affected_users_count = @affected_users[approach]
|
92
|
+
if params[:affected_users] == "many"
|
93
|
+
#approach.weight.to_f / [affected_users_count, 0.1].min
|
94
|
+
approach.weight + (max_affected_users - affected_users_count) * 10
|
95
|
+
else
|
96
|
+
#approach.weight * affected_users_count
|
97
|
+
approach.weight + affected_users_count * 10
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@grouped_approaches = analyzer.group_approaches(@approaches)
|
103
|
+
|
80
104
|
respond_to do |format|
|
81
105
|
format.js do
|
82
106
|
render :partial => 'suggestions'
|
@@ -46,7 +46,7 @@ module AuthorizationRulesHelper
|
|
46
46
|
|
47
47
|
def navigation
|
48
48
|
link_to("Rules", authorization_rules_path) << ' | ' <<
|
49
|
-
link_to("Change
|
49
|
+
link_to("Change Support", change_authorization_rules_path) << ' | ' <<
|
50
50
|
link_to("Graphical view", graph_authorization_rules_path) << ' | ' <<
|
51
51
|
link_to("Usages", authorization_usages_path) #<< ' | ' <<
|
52
52
|
# 'Edit | ' <<
|
@@ -118,8 +118,8 @@ module AuthorizationRulesHelper
|
|
118
118
|
|
119
119
|
def prohibit_link (step, text, title, options)
|
120
120
|
options[:with_removal] ?
|
121
|
-
|
122
|
-
:class => '
|
121
|
+
link_to_function("[x]", "prohibit_action('#{serialize_action(step)}', '#{text}')",
|
122
|
+
:class => 'prohibit', :title => title) :
|
123
123
|
''
|
124
124
|
end
|
125
125
|
|
@@ -149,6 +149,10 @@ module AuthorizationRulesHelper
|
|
149
149
|
@changes && @changes[args[0]] && @changes[args[0]].include?(args[1..-1])
|
150
150
|
end
|
151
151
|
|
152
|
+
def affected_users_count (approach)
|
153
|
+
@affected_users[approach]
|
154
|
+
end
|
155
|
+
|
152
156
|
def auth_usage_info_classes (auth_info)
|
153
157
|
classes = []
|
154
158
|
if auth_info[:controller_permissions]
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<form>
|
2
|
-
<h2>
|
2
|
+
<h2>Which permission to change?</h2>
|
3
3
|
<p class="action-options">
|
4
4
|
<label>Privilege</label>
|
5
5
|
<%= select_tag :privilege, options_for_select(@privileges.map(&:to_s).sort, @privilege.to_s) %>
|
@@ -7,10 +7,19 @@
|
|
7
7
|
<label>On</label>
|
8
8
|
<%= select_tag :context, options_for_select(@contexts.map(&:to_s).sort, @context.to_s) %>
|
9
9
|
<br/>
|
10
|
-
|
10
|
+
<label></label>
|
11
|
+
<%= link_to_function "Show current permissions", "show_current_permissions()", :class => 'unimportant' %>
|
12
|
+
<br/><br/>
|
13
|
+
How many users should be <strong>affected</strong>?
|
14
|
+
<br/>
|
15
|
+
<label></label>
|
16
|
+
<%= radio_button_tag :affected_users, :few, params[:affected_users] == 'few' %>
|
17
|
+
<label class="inline">A <strong>few</strong> users</label>
|
18
|
+
<%= radio_button_tag :affected_users, :many, params[:affected_users] == 'many' %>
|
19
|
+
<label class="inline"><strong>Many</strong> users</label>
|
11
20
|
</p>
|
12
21
|
|
13
|
-
<h2>
|
22
|
+
<h2>Whose permission should be changed?</h2>
|
14
23
|
<table class="change-options">
|
15
24
|
<thead>
|
16
25
|
<tr>
|
@@ -32,6 +32,6 @@
|
|
32
32
|
}
|
33
33
|
<% end %>
|
34
34
|
<div id="graph-container" style="display:none">
|
35
|
-
<%= link_to_function "Hide", "$('graph-container').hide()" %><br/>
|
36
|
-
<object id="graph" data="" type="image/svg+xml" style="max-width:100
|
35
|
+
<%= link_to_function "Hide", "$('graph-container').hide()", :class => 'important' %><br/>
|
36
|
+
<object id="graph" data="" type="image/svg+xml" style="max-width:100%;margin-top: 0.5em"/>
|
37
37
|
</div>
|
@@ -4,21 +4,45 @@
|
|
4
4
|
<% if @approaches.first.changes.empty? %>
|
5
5
|
<p>No changes necessary.</p>
|
6
6
|
<% else %>
|
7
|
-
<p
|
8
|
-
|
7
|
+
<p class="unimportant">
|
8
|
+
<%= pluralize(@approaches.length, 'approach') %> in
|
9
|
+
<%= pluralize(@grouped_approaches.length, 'group') %>
|
10
|
+
<%= params[:affected_users] ? "– #{params[:affected_users] == 'few' ? "fewer" : "most"} affected users first" : "" %>
|
11
|
+
</p>
|
12
|
+
<ul>
|
13
|
+
<% @grouped_approaches.each_with_index do |grouped_approach, group_index| %>
|
14
|
+
<% ([grouped_approach.approach] + grouped_approach.similar_approaches).each_with_index do |approach, index| %>
|
15
|
+
<li <%= (group_index < 3 || params[:show_all]) && index == 0 ? '' : 'style="display: none"' %> class="<%= index == 0 ? 'primary' : "secondary" %> group-<%= group_index %>">
|
16
|
+
<!--<span class="ord"><%= (index + 1) %></span>-->
|
17
|
+
<span class="unimportant">Affected users</span> <strong><%= affected_users_count(approach) %></strong>
|
18
|
+
<span class="unimportant">Complexity</span> <strong><%= approach.weight %></strong>
|
19
|
+
<%= link_to_function "Diagram", "show_suggest_graph('#{serialize_changes(approach)}', '#{serialize_relevant_roles(approach)}', '#{@context}', relevant_user_ids())", :class => "show-approach" %>
|
20
|
+
<ul>
|
21
|
+
<% approach.changes.each do |action| %>
|
22
|
+
<% (action.to_a[0].is_a?(Enumerable) ? action.to_a : [action.to_a]).each do |step| %>
|
23
|
+
<li>
|
24
|
+
<%= describe_step(step.to_a, :with_removal => true) %>
|
25
|
+
</li>
|
26
|
+
<% end %>
|
27
|
+
<% end %>
|
28
|
+
</ul>
|
29
|
+
<% if index == 0 %>
|
30
|
+
<% if grouped_approach.similar_approaches.empty? %>
|
31
|
+
<span class="unimportant show-others-in-group">No further suggestions in this group</span>
|
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" %>
|
34
|
+
<% end %>
|
35
|
+
<% end %>
|
36
|
+
<%= link_to_function "Hide further suggestions", "hide_group_approaches(#{group_index})" if index > 0 and index == grouped_approach.similar_approaches.length %>
|
37
|
+
</li>
|
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>
|
40
|
+
<% end %>
|
41
|
+
<% end %>
|
42
|
+
<% end %>
|
43
|
+
</ul>
|
44
|
+
|
9
45
|
<% end %>
|
10
46
|
<% else %>
|
11
47
|
<p><strong>No approach found.</strong></p>
|
12
48
|
<% end %>
|
13
|
-
|
14
|
-
<% if @approaches.length > 1 %>
|
15
|
-
<p <%= !params[:show_all] ? '' : 'style="display: none"' %>><a href="#" onclick="$(this).up().hide();$('more-suggestions').show();return false">Show other <%= pluralize(@approaches.length - 1, 'approach') %></a></p>
|
16
|
-
<div id="more-suggestions" <%= params[:show_all] ? '' : 'style="display: none"' %>>
|
17
|
-
<% @approaches[1..-1].each_with_index do |approach, index| %>
|
18
|
-
<p>
|
19
|
-
<%= (index + 2).ordinalize %> best approach (<%= pluralize(approach.changes.length, 'step') %>, <%= link_to_function "show", "show_suggest_graph('#{serialize_changes(approach)}', '#{serialize_relevant_roles(approach)}', '#{@context}', relevant_user_ids())" %>)
|
20
|
-
<%= render "suggestion", :object => approach %>
|
21
|
-
</p>
|
22
|
-
<% end %>
|
23
|
-
</div>
|
24
|
-
<% end %>
|
@@ -4,12 +4,13 @@
|
|
4
4
|
<%= link_to_function "Hide", "$(this).up().hide()", :class => 'important' %>
|
5
5
|
<%= link_to_function "Toggle stacked roles", "toggle_graph_params('suggest-graph', 'stacked_roles');" %>
|
6
6
|
<%= link_to_function "Toggle only users' roles", "toggle_graph_params('suggest-graph', 'only_relevant_roles');" %><br/>
|
7
|
-
<object id="suggest-graph" data="" type="image/svg+xml" style="max-width: 98%;max-height: 95
|
7
|
+
<object id="suggest-graph" data="" type="image/svg+xml" style="max-width: 98%;max-height: 95%;margin-top: 0.5em"/>
|
8
8
|
</div>
|
9
9
|
<%= render 'show_graph' %>
|
10
10
|
|
11
11
|
<style type="text/css">
|
12
12
|
.action-options label { display: block; float: left; width: 7em; padding-bottom: 0.5em }
|
13
|
+
.action-options label.inline { display: inline; float: none }
|
13
14
|
.action-options select { float: left; }
|
14
15
|
.action-options br { clear: both; }
|
15
16
|
.action-options { margin-bottom: 2em }
|
@@ -23,21 +24,33 @@
|
|
23
24
|
.submit { margin-top: 0 }
|
24
25
|
.submit input { font-weight: bold; font-size: 120% }
|
25
26
|
#suggest-result {
|
26
|
-
position: absolute; left:
|
27
|
-
border-left:
|
28
|
-
padding-left:
|
27
|
+
position: absolute; left: 55%; right: 10px;
|
28
|
+
border-left: 1px solid grey;
|
29
|
+
padding-left: 2em;
|
29
30
|
z-index: 10;
|
30
31
|
}
|
32
|
+
#suggest-result>ul { padding-left: 0 }
|
33
|
+
#suggest-result>ul>li { list-style: none; margin-bottom: 1em }
|
34
|
+
#suggest-result>ul>li.secondary { padding-left: 2em }
|
35
|
+
#suggest-result li { padding-left: 0 }
|
36
|
+
#suggest-result ul ul { padding-left: 2em }
|
37
|
+
#suggest-result .ord { float: left; display: block; width: 2em; font-weight: bold; color: grey }
|
38
|
+
.show-approach, .show-others-in-group { visibility: hidden }
|
39
|
+
.prohibit { text-decoration: none; visibility: hidden }
|
40
|
+
li:hover .show-approach, li:hover .prohibit,
|
41
|
+
li:hover .show-others-in-group { visibility: visible }
|
31
42
|
#suggest-graph-container {
|
32
|
-
background: white; border:1px solid
|
43
|
+
background: white; border:1px solid grey;
|
33
44
|
position:fixed; z-index: 20;
|
34
|
-
left:10%; bottom: 10%; right: 10%; top: 10
|
45
|
+
left:10%; bottom: 10%; right: 10%; top: 10%;
|
46
|
+
padding: 0.5em
|
35
47
|
}
|
36
48
|
#graph-container {
|
37
|
-
background: white; margin: 1em; border:1px solid
|
49
|
+
background: white; margin: 1em; border:1px solid grey; padding: 0.5em;
|
38
50
|
max-width:50%; position:fixed; z-index: 20; right:0;
|
39
51
|
}
|
40
|
-
.unimportant, .remove { color: grey }
|
52
|
+
.unimportant, .remove, .prohibit { color: grey }
|
53
|
+
.important { font-weight: bold }
|
41
54
|
.remove { cursor: pointer }
|
42
55
|
</style>
|
43
56
|
<% javascript_tag do %>
|
@@ -72,8 +85,9 @@
|
|
72
85
|
function install_change_observers () {
|
73
86
|
$$('#change select').each(function (el) {
|
74
87
|
el.observe('change', function (event) {
|
88
|
+
var form = $('change').down('form');
|
75
89
|
new Ajax.Updater({success: 'change'}, '<%= url_for %>', {
|
76
|
-
parameters:
|
90
|
+
parameters: Form.serializeElements(form.select('select').concat(form.getInputs('radio', 'affected_users')), true),
|
77
91
|
method: 'get',
|
78
92
|
onComplete: function () {
|
79
93
|
install_change_observers();
|
@@ -98,6 +112,20 @@
|
|
98
112
|
show_graph($F('privilege'), $F('context')/*, relevant_user_ids()*/);
|
99
113
|
}
|
100
114
|
|
115
|
+
function show_all_groups () {
|
116
|
+
$$('#suggest-result>ul>li.primary').invoke("show");
|
117
|
+
}
|
118
|
+
|
119
|
+
function show_group_approaches (group_no) {
|
120
|
+
$$('#suggest-result>ul>li.secondary.group-' + group_no).invoke("show");
|
121
|
+
$$('#suggest-result>ul>li.primary.group-' + group_no + ' a.show-others-in-group').invoke("hide");
|
122
|
+
}
|
123
|
+
|
124
|
+
function hide_group_approaches (group_no) {
|
125
|
+
$$('#suggest-result>ul>li.secondary.group-' + group_no).invoke("hide");
|
126
|
+
$$('#suggest-result>ul>li.primary.group-' + group_no + ' a.show-others-in-group').invoke("show");
|
127
|
+
}
|
128
|
+
|
101
129
|
function relevant_user_ids () {
|
102
130
|
return $$('#change .user_id').collect(function (el) {
|
103
131
|
return el.innerHTML;
|
@@ -33,10 +33,10 @@ module Authorization
|
|
33
33
|
Thread.current["current_user"] = user
|
34
34
|
end
|
35
35
|
|
36
|
-
@@ignore_access_control = false
|
37
36
|
# For use in test cases only
|
38
37
|
def self.ignore_access_control (state = nil) # :nodoc:
|
39
|
-
|
38
|
+
Thread.current["ignore_access_control"] = state unless state.nil?
|
39
|
+
Thread.current["ignore_access_control"] || false
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.activate_authorization_rules_browser? # :nodoc:
|
@@ -117,10 +117,9 @@ module Authorization
|
|
117
117
|
# Options:
|
118
118
|
# [:+context+]
|
119
119
|
# The context part of the privilege.
|
120
|
-
# Defaults either to the +
|
121
|
-
# That is,
|
122
|
-
# Raises AuthorizationUsageError if
|
123
|
-
# context is missing and not to be infered.
|
120
|
+
# Defaults either to the tableized +class_name+ of the given :+object+, if given.
|
121
|
+
# That is, :+users+ for :+object+ of type User.
|
122
|
+
# Raises AuthorizationUsageError if context is missing and not to be infered.
|
124
123
|
# [:+object+] An context object to test attribute checks against.
|
125
124
|
# [:+skip_attribute_test+]
|
126
125
|
# Skips those attribute checks in the
|
@@ -153,7 +152,11 @@ module Authorization
|
|
153
152
|
options[:object] = options[:object].new
|
154
153
|
end
|
155
154
|
|
156
|
-
options[:context] ||= options[:object] &&
|
155
|
+
options[:context] ||= options[:object] && (
|
156
|
+
options[:object].class.respond_to?(:decl_auth_context) ?
|
157
|
+
options[:object].class.decl_auth_context :
|
158
|
+
options[:object].class.name.tableize.to_sym
|
159
|
+
) rescue NoMethodError
|
157
160
|
|
158
161
|
user, roles, privileges = user_roles_privleges_from_options(privilege, options)
|
159
162
|
|
@@ -100,7 +100,7 @@ module Authorization
|
|
100
100
|
SMALL_ROLES_RATIO = 0.2
|
101
101
|
|
102
102
|
def analyze_policy
|
103
|
-
small_roles.
|
103
|
+
small_roles.length > 1 and small_roles.length.to_f / roles.length.to_f > SMALL_ROLES_RATIO
|
104
104
|
end
|
105
105
|
|
106
106
|
def message (object)
|
@@ -109,7 +109,7 @@ module Authorization
|
|
109
109
|
|
110
110
|
private
|
111
111
|
def small_roles
|
112
|
-
roles.select {|role| role.rules.
|
112
|
+
roles.select {|role| role.rules.length < SMALL_ROLE_RULES_COUNT }
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -8,7 +8,6 @@ module Authorization
|
|
8
8
|
# * Objective function:
|
9
9
|
# * affected user count,
|
10
10
|
# * as specific as possible (roles, privileges)
|
11
|
-
# -> counter-productive?
|
12
11
|
# * as little changes as necessary
|
13
12
|
# * Modify role, privilege hierarchy
|
14
13
|
# * Merge, split roles
|
@@ -18,7 +17,7 @@ module Authorization
|
|
18
17
|
# * group similar candidates: only show abstract methods?
|
19
18
|
# * restructure GUI layout: more room for analyzing suggestions
|
20
19
|
# * changelog, previous tests, etc.
|
21
|
-
# *
|
20
|
+
# * multiple permissions in tests
|
22
21
|
# * Evaluation of approaches with Analyzer algorithms
|
23
22
|
# * Authorization constraints
|
24
23
|
#
|
@@ -41,6 +40,11 @@ module Authorization
|
|
41
40
|
#
|
42
41
|
class ChangeSupporter < AbstractAnalyzer
|
43
42
|
|
43
|
+
# Returns a list of possible approaches for changes to the current
|
44
|
+
# authorization rules that achieve a given goal. The goal is given as
|
45
|
+
# permission tests in the block. The instance method +users+ is available
|
46
|
+
# when the block is executed to refer to the then-current users, whose
|
47
|
+
# roles might have changed as one suggestion.
|
44
48
|
def find_approaches_for (options, &tests)
|
45
49
|
@prohibited_actions = (options[:prohibited_actions] || []).to_set
|
46
50
|
|
@@ -63,10 +67,29 @@ module Authorization
|
|
63
67
|
end
|
64
68
|
|
65
69
|
# remove subsets
|
66
|
-
|
67
70
|
suggestions.sort!
|
68
71
|
end
|
69
72
|
|
73
|
+
# Returns an array of GroupedApproaches for the given array of approaches.
|
74
|
+
# Only groups directly adjacent approaches
|
75
|
+
def group_approaches (approaches)
|
76
|
+
approaches.each_with_object([]) do |approach, grouped|
|
77
|
+
if grouped.last and grouped.last.approach.similar_to(approach)
|
78
|
+
grouped.last.similar_approaches << approach
|
79
|
+
else
|
80
|
+
grouped << GroupedApproach.new(approach)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class GroupedApproach
|
86
|
+
attr_accessor :approach, :similar_approaches
|
87
|
+
def initialize (approach)
|
88
|
+
@approach = approach
|
89
|
+
@similar_approaches = []
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
70
93
|
class ApproachChecker
|
71
94
|
attr_reader :users, :failed_tests
|
72
95
|
|
@@ -119,6 +142,15 @@ module Authorization
|
|
119
142
|
res
|
120
143
|
end
|
121
144
|
|
145
|
+
def affected_users (original_engine, original_users, privilege, context)
|
146
|
+
(0...@users.length).select do |i|
|
147
|
+
original_engine.permit?(privilege, :context => context,
|
148
|
+
:skip_attribute_test => true, :user => original_users[i]) !=
|
149
|
+
@engine.permit?(privilege, :context => context,
|
150
|
+
:skip_attribute_test => true, :user => @users[i])
|
151
|
+
end.collect {|i| original_users[i]}
|
152
|
+
end
|
153
|
+
|
122
154
|
def initialize_copy (other)
|
123
155
|
@engine = @engine.clone
|
124
156
|
@users = @users.clone
|
@@ -171,7 +203,17 @@ module Authorization
|
|
171
203
|
end
|
172
204
|
|
173
205
|
def sort_value
|
174
|
-
|
206
|
+
weight + @failed_tests.length
|
207
|
+
end
|
208
|
+
|
209
|
+
def weight
|
210
|
+
changes.sum(&:weight)
|
211
|
+
end
|
212
|
+
|
213
|
+
def similar_to (other)
|
214
|
+
other.weight == weight and
|
215
|
+
other.changes.map {|change| change.class.name}.sort ==
|
216
|
+
changes.map {|change| change.class.name}.sort
|
175
217
|
end
|
176
218
|
|
177
219
|
def inspect
|