stffn-declarative_authorization 0.3.0 → 0.3.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 +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
|