stffn-declarative_authorization 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -0
- data/README.rdoc +22 -6
- data/app/controllers/authorization_rules_controller.rb +135 -14
- data/app/helpers/authorization_rules_helper.rb +96 -13
- data/app/views/authorization_rules/_change.erb +49 -0
- data/app/views/authorization_rules/_show_graph.erb +37 -0
- data/app/views/authorization_rules/_suggestion.erb +9 -0
- data/app/views/authorization_rules/_suggestions.erb +24 -0
- data/app/views/authorization_rules/change.html.erb +124 -0
- data/app/views/authorization_rules/graph.dot.erb +23 -4
- data/app/views/authorization_rules/graph.html.erb +1 -0
- data/app/views/authorization_rules/index.html.erb +3 -2
- data/app/views/authorization_usages/index.html.erb +2 -11
- data/config/routes.rb +2 -1
- data/lib/declarative_authorization/authorization.rb +87 -35
- data/lib/declarative_authorization/development_support/analyzer.rb +252 -0
- data/lib/declarative_authorization/development_support/change_analyzer.rb +253 -0
- data/lib/declarative_authorization/development_support/change_supporter.rb +578 -0
- data/lib/declarative_authorization/development_support/development_support.rb +243 -0
- data/lib/declarative_authorization/helper.rb +6 -2
- data/lib/declarative_authorization/in_controller.rb +254 -26
- data/lib/declarative_authorization/in_model.rb +27 -2
- data/lib/declarative_authorization/maintenance.rb +22 -8
- data/lib/declarative_authorization/obligation_scope.rb +14 -9
- data/lib/declarative_authorization/reader.rb +10 -2
- data/test/authorization_test.rb +44 -0
- data/test/controller_filter_resource_access_test.rb +385 -0
- data/test/controller_test.rb +14 -6
- data/test/helper_test.rb +21 -0
- data/test/maintenance_test.rb +26 -0
- data/test/model_test.rb +28 -0
- data/test/test_helper.rb +14 -1
- metadata +15 -5
- data/lib/declarative_authorization/authorization_rules_analyzer.rb +0 -138
- data/test/authorization_rules_analyzer_test.rb +0 -123
@@ -0,0 +1,37 @@
|
|
1
|
+
<% javascript_tag do %>
|
2
|
+
function show_graph (privilege, context, user_ids) {
|
3
|
+
var params = {
|
4
|
+
privilege_hierarchy: 1,
|
5
|
+
highlight_privilege: privilege,
|
6
|
+
filter_contexts: context
|
7
|
+
};
|
8
|
+
if (user_ids)
|
9
|
+
params['user_ids[]'] = user_ids;
|
10
|
+
show_graph_with_params('graph', params);
|
11
|
+
}
|
12
|
+
|
13
|
+
var graph_params = {};
|
14
|
+
function show_graph_with_params (graph_id, params) {
|
15
|
+
graph_params[graph_id] = params;
|
16
|
+
base_url = "<%= graph_authorization_rules_path('svg') %>";
|
17
|
+
$(graph_id).data = base_url + '?' + Object.toQueryString(params);
|
18
|
+
$(graph_id).up().show();
|
19
|
+
}
|
20
|
+
|
21
|
+
function update_graph_params (graph_id, params) {
|
22
|
+
show_graph_with_params(graph_id,
|
23
|
+
$H(graph_params[graph_id] || {}).merge(params).toObject());
|
24
|
+
}
|
25
|
+
|
26
|
+
function toggle_graph_params (graph_id) {
|
27
|
+
var opts = {}
|
28
|
+
$A(arguments).slice(1).each(function (param) {
|
29
|
+
opts[param] = graph_params[graph_id][param] ? null : '1';
|
30
|
+
});
|
31
|
+
update_graph_params(graph_id, opts)
|
32
|
+
}
|
33
|
+
<% end %>
|
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%"/>
|
37
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<h2>Suggestions</h2>
|
2
|
+
|
3
|
+
<% if @approaches.length > 0 %>
|
4
|
+
<% if @approaches.first.changes.empty? %>
|
5
|
+
<p>No changes necessary.</p>
|
6
|
+
<% else %>
|
7
|
+
<p>Suggested changes (<%= link_to_function "show", "show_suggest_graph('#{serialize_changes(@approaches.first)}', '#{serialize_relevant_roles(@approaches.first)}', '#{@context}', relevant_user_ids())" %>):</p>
|
8
|
+
<%= render "suggestion", :object => @approaches.first %>
|
9
|
+
<% end %>
|
10
|
+
<% else %>
|
11
|
+
<p><strong>No approach found.</strong></p>
|
12
|
+
<% 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 %>
|
@@ -0,0 +1,124 @@
|
|
1
|
+
<h1>Suggestions on Authorization Rules Change</h1>
|
2
|
+
<p><%= navigation %></p>
|
3
|
+
<div style="display:none" id="suggest-graph-container">
|
4
|
+
<%= link_to_function "Hide", "$(this).up().hide()", :class => 'important' %>
|
5
|
+
<%= link_to_function "Toggle stacked roles", "toggle_graph_params('suggest-graph', 'stacked_roles');" %>
|
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%"/>
|
8
|
+
</div>
|
9
|
+
<%= render 'show_graph' %>
|
10
|
+
|
11
|
+
<style type="text/css">
|
12
|
+
.action-options label { display: block; float: left; width: 7em; padding-bottom: 0.5em }
|
13
|
+
.action-options select { float: left; }
|
14
|
+
.action-options br { clear: both; }
|
15
|
+
.action-options { margin-bottom: 2em }
|
16
|
+
.change-options { margin-bottom: 1em }
|
17
|
+
.change-options td.user_id { display: none }
|
18
|
+
.change-options td { padding-right: 1em; padding-left: 0.5em }
|
19
|
+
.change-options thead td { border-bottom: 1px solid lightgrey }
|
20
|
+
.change-options thead td.choose { text-align: center; font-weight: bold }
|
21
|
+
.change-options tr.permitted td.yes,
|
22
|
+
.change-options tr.not-permitted td.no { background: #FFE599 }
|
23
|
+
.submit { margin-top: 0 }
|
24
|
+
.submit input { font-weight: bold; font-size: 120% }
|
25
|
+
#suggest-result {
|
26
|
+
position: absolute; left: 60%; right: 10px;
|
27
|
+
border-left: 2px solid grey;
|
28
|
+
padding-left: 1em;
|
29
|
+
z-index: 10;
|
30
|
+
}
|
31
|
+
#suggest-graph-container {
|
32
|
+
background: white; border:1px solid #ccc;
|
33
|
+
position:fixed; z-index: 20;
|
34
|
+
left:10%; bottom: 10%; right: 10%; top: 10%
|
35
|
+
}
|
36
|
+
#graph-container {
|
37
|
+
background: white; margin: 1em; border:1px solid #ccc;
|
38
|
+
max-width:50%; position:fixed; z-index: 20; right:0;
|
39
|
+
}
|
40
|
+
.unimportant, .remove { color: grey }
|
41
|
+
.remove { cursor: pointer }
|
42
|
+
</style>
|
43
|
+
<% javascript_tag do %>
|
44
|
+
function suggest_changes (refresh) {
|
45
|
+
if (!refresh)
|
46
|
+
$("suggest-result").innerHTML = "Searching...";
|
47
|
+
$("suggest-result").show();
|
48
|
+
new Ajax.Updater({success: 'suggest-result'}, '<%= suggest_change_authorization_rules_path %>', {
|
49
|
+
method: 'get',
|
50
|
+
onFailure: function(request) {
|
51
|
+
$("suggest-result").innerHTML = "Error while searching."
|
52
|
+
},
|
53
|
+
parameters: $H($('change').down('form').serialize(true)).merge(refresh ? {show_all: "true"} : {}).toQueryString()
|
54
|
+
});
|
55
|
+
if (!refresh)
|
56
|
+
location.hash = 'suggest-result';
|
57
|
+
}
|
58
|
+
|
59
|
+
function show_suggest_graph (changes, filter_roles_params, filter_context, user_ids) {
|
60
|
+
var params = {changes: changes, highlight_privilege: $F('privilege')};
|
61
|
+
if (filter_context)
|
62
|
+
params['filter_contexts'] = filter_context;
|
63
|
+
if (user_ids)
|
64
|
+
params['user_ids[]'] = user_ids;
|
65
|
+
show_graph_with_params('suggest-graph', params);
|
66
|
+
}
|
67
|
+
|
68
|
+
document.observe('dom:loaded', function() {
|
69
|
+
install_change_observers();
|
70
|
+
});
|
71
|
+
|
72
|
+
function install_change_observers () {
|
73
|
+
$$('#change select').each(function (el) {
|
74
|
+
el.observe('change', function (event) {
|
75
|
+
new Ajax.Updater({success: 'change'}, '<%= url_for %>', {
|
76
|
+
parameters: { context: $F('context'), privilege: $F('privilege') },
|
77
|
+
method: 'get',
|
78
|
+
onComplete: function () {
|
79
|
+
install_change_observers();
|
80
|
+
if ($('graph-container').visible())
|
81
|
+
show_current_permissions();
|
82
|
+
}
|
83
|
+
});
|
84
|
+
});
|
85
|
+
});
|
86
|
+
$('prohibited_actions').observe('click', function (event) {
|
87
|
+
var target = event.findElement();
|
88
|
+
if (target.hasClassName('remove')) {
|
89
|
+
target.up().remove();
|
90
|
+
if ($('prohibited_actions').childElements().length == 0)
|
91
|
+
$('prohibited_actions').previous().hide();
|
92
|
+
suggest_changes();
|
93
|
+
}
|
94
|
+
});
|
95
|
+
}
|
96
|
+
|
97
|
+
function show_current_permissions () {
|
98
|
+
show_graph($F('privilege'), $F('context')/*, relevant_user_ids()*/);
|
99
|
+
}
|
100
|
+
|
101
|
+
function relevant_user_ids () {
|
102
|
+
return $$('#change .user_id').collect(function (el) {
|
103
|
+
return el.innerHTML;
|
104
|
+
}).reject(function (id) {
|
105
|
+
return $('user_' + id + '_permission_undetermined').checked;
|
106
|
+
});
|
107
|
+
}
|
108
|
+
|
109
|
+
var prohibited_action_template =
|
110
|
+
new Template('<li>#{description} <span class="remove">[x]</span><input type="hidden" name="prohibited_action[]" value="#{action}"></li>');
|
111
|
+
function prohibit_action (action, description) {
|
112
|
+
$('prohibited_actions').previous().show();
|
113
|
+
$('prohibited_actions').insert(
|
114
|
+
{bottom: prohibited_action_template.evaluate({action: action, description: description}) }
|
115
|
+
);
|
116
|
+
suggest_changes(true);
|
117
|
+
}
|
118
|
+
<% end %>
|
119
|
+
|
120
|
+
<div id="suggest-result" style="display:none"></div>
|
121
|
+
|
122
|
+
<div id="change">
|
123
|
+
<%= render 'change' %>
|
124
|
+
</div>
|
@@ -7,20 +7,39 @@ digraph rules {
|
|
7
7
|
ranksep = "0.3"
|
8
8
|
//concentrate = true
|
9
9
|
rankdir = TB
|
10
|
+
|
11
|
+
<% unless @users.blank? %>
|
12
|
+
{
|
13
|
+
rank = source
|
14
|
+
node [shape=polygon,style=filled,fillcolor="#eeeeee"]
|
15
|
+
<% @users.each do |user| %>
|
16
|
+
"<%= user.login %>"
|
17
|
+
<% end %>
|
18
|
+
}
|
19
|
+
<% end %>
|
20
|
+
|
10
21
|
{
|
11
22
|
node [shape=ellipse,style=filled]
|
12
|
-
|
23
|
+
<%= @stacked_roles ? '' : "rank = same" %>
|
13
24
|
<% @roles.each do |role| %>
|
14
25
|
"<%= role.inspect %>" [fillcolor="<%= role_fill_color(role) %>"]
|
15
26
|
// ,URL="javascript:set_filter({roles: '<%= role %>'})"
|
16
27
|
<% end %>
|
17
28
|
<% @roles.each do |role| %>
|
18
|
-
<% (@role_hierarchy[role] || []).each do |lower_role| %>
|
19
|
-
"<%= role.inspect %>" -> "<%= lower_role.inspect %>" [
|
29
|
+
<% (@role_hierarchy[role] || []).select {|lower_role| @roles.include?(lower_role)}.each do |lower_role| %>
|
30
|
+
"<%= role.inspect %>" -> "<%= lower_role.inspect %>" [arrowhead=empty]
|
20
31
|
<% end %>
|
21
32
|
<% end %>
|
22
33
|
}
|
23
34
|
|
35
|
+
<% unless @users.blank? %>
|
36
|
+
<% @users.each do |user| %>
|
37
|
+
<% user.role_symbols.select {|role| @roles.include?(role)}.each do |role| %>
|
38
|
+
"<%= user.login %>" -> "<%= role.inspect %>" [color="<%= has_changed(:assign_role_to_user, role, user.login) ? '#00dd00' : (has_changed(:remove_role_from_user, role, user.login) ? '#dd0000' : '#000000') %>"]
|
39
|
+
<% end %>
|
40
|
+
<% end %>
|
41
|
+
<% end %>
|
42
|
+
|
24
43
|
<% @contexts.each do |context| %>
|
25
44
|
subgraph cluster_<%= context %> {
|
26
45
|
label = "<%= context.inspect %>"
|
@@ -43,7 +62,7 @@ digraph rules {
|
|
43
62
|
|
44
63
|
<% @roles.each do |role| %>
|
45
64
|
<% (@role_privs[role] || []).each do |context, privilege, unconditionally, attribute_string| %>
|
46
|
-
"<%= role.inspect %>" -> <%= privilege %>_<%= context %> [color="<%=
|
65
|
+
"<%= role.inspect %>" -> <%= privilege %>_<%= context %> [color="<%= privilege_color(privilege, context, role) %>", minlen=3<%= ", arrowhead=opendot, URL=\"javascript:\", edgetooltip=\"#{attribute_string.gsub('"','')}\"" unless unconditionally %>]
|
47
66
|
<% end %>
|
48
67
|
<% end %>
|
49
68
|
}
|
@@ -30,6 +30,7 @@
|
|
30
30
|
<%= select_tag "filter_contexts", options_for_select([["All contexts",'']] + controller.authorization_engine.auth_rules.collect {|ar| ar.contexts.to_a}.flatten.uniq.map(&:to_s).sort), :onchange => 'update_graph(this.form)' %>
|
31
31
|
<%= check_box_tag "effective_role_privs", "1", false, :onclick => 'update_graph(this.form)' %> <%= label_tag "effective_role_privs", "Effective privileges" %>
|
32
32
|
<%= check_box_tag "privilege_hierarchy", "1", false, :onclick => 'update_graph(this.form)' %> <%= label_tag "privilege_hierarchy", "Show full privilege hierarchy" %>
|
33
|
+
<%= check_box_tag "stacked_roles", "1", false, :onclick => 'update_graph(this.form)' %> <%= label_tag "stacked_roles", "Stacked roles" %>
|
33
34
|
<% end %>
|
34
35
|
</p>
|
35
36
|
<div style="margin: 1em;border:1px solid #ccc;max-width:95%">
|
@@ -9,8 +9,9 @@
|
|
9
9
|
pre .proc {color: #0a0;}
|
10
10
|
pre .privilege, pre .context {font-weight: bold}
|
11
11
|
pre .preproc, pre .comment, pre .comment span {color: grey !important;}
|
12
|
-
pre .note {color: #a00; position:absolute; cursor: help }
|
12
|
+
pre .note {color: #a00; position:absolute; cursor: help; left: 15px }
|
13
|
+
pre.with-notes {padding-left: 35px}
|
13
14
|
</style>
|
14
|
-
<pre>
|
15
|
+
<pre class="with-notes">
|
15
16
|
<%= policy_analysis_hints(syntax_highlight(h(@auth_rules_script)), @auth_rules_script) %>
|
16
17
|
</pre>
|
@@ -1,7 +1,5 @@
|
|
1
1
|
<h1>Authorization Usage</h1>
|
2
|
-
|
3
|
-
<object id="graph" data="<%= url_for :format => 'svg' %>" type="image/svg+xml" style="max-width:100%"/>
|
4
|
-
</div>
|
2
|
+
<%= render 'authorization_rules/show_graph' %>
|
5
3
|
<p>Filter rules in actions by controller:</p>
|
6
4
|
<p><%= navigation %></p>
|
7
5
|
<style type="text/css">
|
@@ -13,15 +11,8 @@
|
|
13
11
|
/*.auth-usages tr.catch-all td.privilege,*/
|
14
12
|
.auth-usages tr.default-privilege td.privilege,
|
15
13
|
.auth-usages tr.default-context td.context { color: #888888 }
|
14
|
+
#graph-container {margin: 1em; border:1px solid #ccc; max-width:50%; position:fixed; right:0;}
|
16
15
|
</style>
|
17
|
-
<% javascript_tag do %>
|
18
|
-
function show_graph (privilege, context) {
|
19
|
-
base_url = "<%= graph_authorization_rules_path('svg') %>";
|
20
|
-
$('graph').data = base_url + '?privilege_hierarchy=1&highlight_privilege=' +
|
21
|
-
privilege + '&filter_contexts=' + context;
|
22
|
-
$('graph').up().show();
|
23
|
-
}
|
24
|
-
<% end %>
|
25
16
|
<table class="auth-usages">
|
26
17
|
<% @auth_usages_by_controller.keys.sort {|c1, c2| c1.name <=> c2.name}.each do |controller| %>
|
27
18
|
<% default_context = controller.controller_name.to_sym rescue nil %>
|
data/config/routes.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
ActionController::Routing::Routes.draw do |map|
|
2
2
|
if Authorization::activate_authorization_rules_browser?
|
3
|
-
map.resources :authorization_rules, :only => :index,
|
3
|
+
map.resources :authorization_rules, :only => [:index],
|
4
|
+
:collection => {:graph => :get, :change => :get, :suggest_change => :get}
|
4
5
|
map.resources :authorization_usages, :only => :index
|
5
6
|
end
|
6
7
|
end
|
@@ -58,7 +58,8 @@ module Authorization
|
|
58
58
|
#
|
59
59
|
class Engine
|
60
60
|
attr_reader :roles, :role_titles, :role_descriptions, :privileges,
|
61
|
-
:privilege_hierarchy, :auth_rules, :role_hierarchy, :rev_priv_hierarchy
|
61
|
+
:privilege_hierarchy, :auth_rules, :role_hierarchy, :rev_priv_hierarchy,
|
62
|
+
:rev_role_hierarchy
|
62
63
|
|
63
64
|
# If +reader+ is not given, a new one is created with the default
|
64
65
|
# authorization configuration of +AUTH_DSL_FILE+. If given, may be either
|
@@ -82,6 +83,7 @@ module Authorization
|
|
82
83
|
|
83
84
|
@role_titles = reader.auth_rules_reader.role_titles
|
84
85
|
@role_descriptions = reader.auth_rules_reader.role_descriptions
|
86
|
+
@reader = reader
|
85
87
|
|
86
88
|
# {[priv, ctx] => [priv, ...]}
|
87
89
|
@rev_priv_hierarchy = {}
|
@@ -91,6 +93,20 @@ module Authorization
|
|
91
93
|
@rev_priv_hierarchy[val] << key
|
92
94
|
end
|
93
95
|
end
|
96
|
+
@rev_role_hierarchy = {}
|
97
|
+
@role_hierarchy.each do |higher_role, lower_roles|
|
98
|
+
lower_roles.each do |role|
|
99
|
+
(@rev_role_hierarchy[role] ||= []) << higher_role
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize_copy (from) # :nodoc:
|
105
|
+
[
|
106
|
+
:privileges, :privilege_hierarchy, :roles, :role_hierarchy, :role_titles,
|
107
|
+
:role_descriptions, :rev_priv_hierarchy, :rev_role_hierarchy
|
108
|
+
].each {|attr| instance_variable_set(:"@#{attr}", from.send(attr).clone) }
|
109
|
+
@auth_rules = from.auth_rules.collect {|rule| rule.clone}
|
94
110
|
end
|
95
111
|
|
96
112
|
# Returns true if privilege is met by the current user. Raises
|
@@ -143,7 +159,7 @@ module Authorization
|
|
143
159
|
|
144
160
|
# find a authorization rule that matches for at least one of the roles and
|
145
161
|
# at least one of the given privileges
|
146
|
-
attr_validator = AttributeValidator.new(self, user, options[:object])
|
162
|
+
attr_validator = AttributeValidator.new(self, user, options[:object], privilege, options[:context])
|
147
163
|
rules = matching_auth_rules(roles, privileges, options[:context])
|
148
164
|
if rules.empty?
|
149
165
|
raise NotAuthorized, "No matching rules found for #{privilege} for #{user.inspect} " +
|
@@ -152,21 +168,8 @@ module Authorization
|
|
152
168
|
end
|
153
169
|
|
154
170
|
# Test each rule in turn to see whether any one of them is satisfied.
|
155
|
-
|
156
|
-
|
157
|
-
options[:skip_attribute_test] or
|
158
|
-
rule.attributes.empty? or
|
159
|
-
rule.attributes.send(rule.join_operator == :and ? :all? : :any?) do |attr|
|
160
|
-
begin
|
161
|
-
attr.validate?( attr_validator )
|
162
|
-
rescue NilAttributeValueError => e
|
163
|
-
nil # Bumping up against a nil attribute value flunks the rule.
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
unless grant_permission
|
169
|
-
raise AttributeAuthorizationError, "#{privilege} not allowed for #{user.inspect} on #{options[:object].inspect}."
|
171
|
+
unless rules.any? {|rule| rule.validate?(attr_validator, options[:skip_attribute_test])}
|
172
|
+
raise AttributeAuthorizationError, "#{privilege} not allowed for #{user.inspect} on #{(options[:object] || options[:context]).inspect}."
|
170
173
|
end
|
171
174
|
true
|
172
175
|
end
|
@@ -201,17 +204,9 @@ module Authorization
|
|
201
204
|
def obligations (privilege, options = {})
|
202
205
|
options = {:context => nil}.merge(options)
|
203
206
|
user, roles, privileges = user_roles_privleges_from_options(privilege, options)
|
204
|
-
attr_validator = AttributeValidator.new(self, user, nil, options[:context])
|
207
|
+
attr_validator = AttributeValidator.new(self, user, nil, privilege, options[:context])
|
205
208
|
matching_auth_rules(roles, privileges, options[:context]).collect do |rule|
|
206
|
-
|
207
|
-
if rule.join_operator == :and and !obligations.empty?
|
208
|
-
merged_obligation = obligations.first
|
209
|
-
obligations[1..-1].each do |obligation|
|
210
|
-
merged_obligation = merged_obligation.deep_merge(obligation)
|
211
|
-
end
|
212
|
-
obligations = [merged_obligation]
|
213
|
-
end
|
214
|
-
obligations.empty? ? [{}] : obligations
|
209
|
+
rule.obligations(attr_validator)
|
215
210
|
end.flatten
|
216
211
|
end
|
217
212
|
|
@@ -263,11 +258,12 @@ module Authorization
|
|
263
258
|
end
|
264
259
|
|
265
260
|
class AttributeValidator # :nodoc:
|
266
|
-
attr_reader :user, :object, :engine, :context
|
267
|
-
def initialize (engine, user, object = nil, context = nil)
|
261
|
+
attr_reader :user, :object, :engine, :context, :privilege
|
262
|
+
def initialize (engine, user, object = nil, privilege = nil, context = nil)
|
268
263
|
@engine = engine
|
269
264
|
@user = user
|
270
265
|
@object = object
|
266
|
+
@privilege = privilege
|
271
267
|
@context = context
|
272
268
|
end
|
273
269
|
|
@@ -281,15 +277,16 @@ module Authorization
|
|
281
277
|
def user_roles_privleges_from_options(privilege, options)
|
282
278
|
options = {
|
283
279
|
:user => nil,
|
284
|
-
:context => nil
|
280
|
+
:context => nil,
|
281
|
+
:user_roles => nil
|
285
282
|
}.merge(options)
|
286
283
|
user = options[:user] || Authorization.current_user
|
287
284
|
privileges = privilege.is_a?(Array) ? privilege : [privilege]
|
288
285
|
|
289
|
-
raise AuthorizationUsageError, "No user object given (#{user.inspect})"
|
290
|
-
unless user
|
286
|
+
raise AuthorizationUsageError, "No user object given (#{user.inspect}) or " +
|
287
|
+
"set through Authorization.current_user" unless user
|
291
288
|
|
292
|
-
roles = flatten_roles(roles_for(user))
|
289
|
+
roles = options[:user_roles] || flatten_roles(roles_for(user))
|
293
290
|
privileges = flatten_privileges privileges, options[:context]
|
294
291
|
[user, roles, privileges]
|
295
292
|
end
|
@@ -329,14 +326,24 @@ module Authorization
|
|
329
326
|
end
|
330
327
|
|
331
328
|
class AuthorizationRule
|
332
|
-
attr_reader :attributes, :contexts, :role, :privileges, :join_operator
|
329
|
+
attr_reader :attributes, :contexts, :role, :privileges, :join_operator,
|
330
|
+
:source_file, :source_line
|
333
331
|
|
334
|
-
def initialize (role, privileges = [], contexts = nil, join_operator = :or
|
332
|
+
def initialize (role, privileges = [], contexts = nil, join_operator = :or,
|
333
|
+
options = {})
|
335
334
|
@role = role
|
336
335
|
@privileges = Set.new(privileges)
|
337
336
|
@contexts = Set.new((contexts && !contexts.is_a?(Array) ? [contexts] : contexts))
|
338
337
|
@join_operator = join_operator
|
339
338
|
@attributes = []
|
339
|
+
@source_file = options[:source_file]
|
340
|
+
@source_line = options[:source_line]
|
341
|
+
end
|
342
|
+
|
343
|
+
def initialize_copy (from)
|
344
|
+
@privileges = @privileges.clone
|
345
|
+
@contexts = @contexts.clone
|
346
|
+
@attributes = @attributes.collect {|attribute| attribute.clone }
|
340
347
|
end
|
341
348
|
|
342
349
|
def append_privileges (privs)
|
@@ -353,6 +360,29 @@ module Authorization
|
|
353
360
|
not (@privileges & privs).empty?
|
354
361
|
end
|
355
362
|
|
363
|
+
def validate? (attr_validator, skip_attribute = false)
|
364
|
+
skip_attribute or @attributes.empty? or
|
365
|
+
@attributes.send(@join_operator == :and ? :all? : :any?) do |attr|
|
366
|
+
begin
|
367
|
+
attr.validate?(attr_validator)
|
368
|
+
rescue NilAttributeValueError => e
|
369
|
+
nil # Bumping up against a nil attribute value flunks the rule.
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def obligations (attr_validator)
|
375
|
+
obligations = @attributes.collect {|attr| attr.obligation(attr_validator) }.flatten
|
376
|
+
if @join_operator == :and and !obligations.empty?
|
377
|
+
merged_obligation = obligations.first
|
378
|
+
obligations[1..-1].each do |obligation|
|
379
|
+
merged_obligation = merged_obligation.deep_merge(obligation)
|
380
|
+
end
|
381
|
+
obligations = [merged_obligation]
|
382
|
+
end
|
383
|
+
obligations.empty? ? [{}] : obligations
|
384
|
+
end
|
385
|
+
|
356
386
|
def to_long_s
|
357
387
|
attributes.collect {|attr| attr.to_long_s } * "; "
|
358
388
|
end
|
@@ -365,6 +395,10 @@ module Authorization
|
|
365
395
|
def initialize (conditions_hash)
|
366
396
|
@conditions_hash = conditions_hash
|
367
397
|
end
|
398
|
+
|
399
|
+
def initialize_copy (from)
|
400
|
+
@conditions_hash = deep_hash_clone(@conditions_hash)
|
401
|
+
end
|
368
402
|
|
369
403
|
def validate? (attr_validator, object = nil, hash = nil)
|
370
404
|
object ||= attr_validator.object
|
@@ -480,6 +514,20 @@ module Authorization
|
|
480
514
|
"#{object.inspect} for validating attribute: #{e}"
|
481
515
|
end
|
482
516
|
end
|
517
|
+
|
518
|
+
def deep_hash_clone (hash)
|
519
|
+
hash.inject({}) do |memo, (key, val)|
|
520
|
+
memo[key] = case val
|
521
|
+
when Hash
|
522
|
+
deep_hash_clone(val)
|
523
|
+
when NilClass, Symbol
|
524
|
+
val
|
525
|
+
else
|
526
|
+
val.clone
|
527
|
+
end
|
528
|
+
memo
|
529
|
+
end
|
530
|
+
end
|
483
531
|
end
|
484
532
|
|
485
533
|
# An attribute condition that uses existing rules to decide validation
|
@@ -493,6 +541,10 @@ module Authorization
|
|
493
541
|
@attr_hash = attr_or_hash
|
494
542
|
end
|
495
543
|
|
544
|
+
def initialize_copy (from)
|
545
|
+
@attr_hash = deep_hash_clone(@attr_hash) if @attr_hash.is_a?(Hash)
|
546
|
+
end
|
547
|
+
|
496
548
|
def validate? (attr_validator, object = nil, hash_or_attr = nil)
|
497
549
|
object ||= attr_validator.object
|
498
550
|
hash_or_attr ||= @attr_hash
|