uhees-declarative_authorization 0.3.1 → 0.3.2.2.1
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 +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
|