annotation_security 1.0.2 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/CHANGELOG +22 -0
  2. data/HOW-TO +261 -0
  3. data/{LICENSE → MIT-LICENSE} +1 -1
  4. data/README +39 -0
  5. data/Rakefile +53 -62
  6. data/assets/app/helpers/annotation_security_helper.rb +8 -8
  7. data/assets/config/initializers/annotation_security.rb +11 -11
  8. data/assets/config/security/relations.rb +20 -20
  9. data/assets/vendor/plugins/annotation_security/init.rb +14 -14
  10. data/bin/annotation_security +7 -7
  11. data/lib/annotation_security.rb +94 -103
  12. data/lib/annotation_security/exceptions.rb +124 -124
  13. data/lib/annotation_security/exec.rb +188 -188
  14. data/lib/annotation_security/includes/helper.rb +215 -215
  15. data/lib/annotation_security/includes/resource.rb +84 -84
  16. data/lib/annotation_security/includes/role.rb +30 -30
  17. data/lib/annotation_security/includes/user.rb +26 -26
  18. data/lib/annotation_security/manager/policy_factory.rb +29 -29
  19. data/lib/annotation_security/manager/policy_manager.rb +87 -79
  20. data/lib/annotation_security/manager/relation_loader.rb +272 -272
  21. data/lib/annotation_security/manager/resource_manager.rb +36 -36
  22. data/lib/annotation_security/manager/right_loader.rb +87 -87
  23. data/lib/annotation_security/policy/abstract_policy.rb +344 -344
  24. data/lib/annotation_security/policy/abstract_static_policy.rb +75 -75
  25. data/lib/annotation_security/policy/all_resources_policy.rb +20 -20
  26. data/lib/annotation_security/policy/rule.rb +340 -340
  27. data/lib/annotation_security/policy/rule_set.rb +138 -138
  28. data/lib/annotation_security/rails.rb +22 -39
  29. data/lib/{extensions → annotation_security/rails/2/extensions}/filter.rb +131 -133
  30. data/lib/annotation_security/rails/2/includes/action_controller.rb +144 -0
  31. data/lib/annotation_security/rails/2/includes/active_record.rb +28 -0
  32. data/lib/annotation_security/rails/2/initializer.rb +35 -0
  33. data/lib/annotation_security/{model_observer.rb → rails/2/model_observer.rb} +61 -61
  34. data/lib/annotation_security/rails/3/extensions/filter.rb +28 -0
  35. data/lib/annotation_security/{includes → rails/3/includes}/action_controller.rb +143 -144
  36. data/lib/annotation_security/{includes → rails/3/includes}/active_record.rb +27 -27
  37. data/lib/annotation_security/rails/3/initializer.rb +40 -0
  38. data/lib/annotation_security/rails/3/model_observer.rb +61 -0
  39. data/lib/annotation_security/rails/extensions.rb +21 -0
  40. data/lib/{extensions → annotation_security/rails/extensions}/action_controller.rb +31 -32
  41. data/lib/{extensions → annotation_security/rails/extensions}/active_record.rb +33 -34
  42. data/lib/{extensions → annotation_security/rails/extensions}/object.rb +10 -10
  43. data/lib/annotation_security/{filters.rb → rails/filters.rb} +37 -37
  44. data/lib/annotation_security/user_wrapper.rb +73 -73
  45. data/lib/annotation_security/utils.rb +141 -141
  46. data/lib/security_context.rb +588 -589
  47. data/spec/annotation_security/exceptions_spec.rb +16 -16
  48. data/spec/annotation_security/includes/helper_spec.rb +82 -82
  49. data/spec/annotation_security/manager/policy_manager_spec.rb +15 -15
  50. data/spec/annotation_security/manager/resource_manager_spec.rb +17 -17
  51. data/spec/annotation_security/manager/right_loader_spec.rb +17 -17
  52. data/spec/annotation_security/policy/abstract_policy_spec.rb +16 -16
  53. data/spec/annotation_security/policy/all_resources_policy_spec.rb +24 -24
  54. data/spec/annotation_security/policy/rule_set_spec.rb +112 -112
  55. data/spec/annotation_security/policy/rule_spec.rb +77 -77
  56. data/spec/annotation_security/policy/test_policy_spec.rb +80 -80
  57. data/spec/annotation_security/security_context_spec.rb +129 -78
  58. data/spec/annotation_security/utils_spec.rb +73 -73
  59. data/spec/helper/test_controller.rb +65 -65
  60. data/spec/helper/test_helper.rb +5 -5
  61. data/spec/helper/test_relations.rb +6 -6
  62. data/spec/helper/test_resource.rb +38 -38
  63. data/spec/helper/test_role.rb +21 -21
  64. data/spec/helper/test_user.rb +31 -31
  65. data/spec/rails_stub.rb +44 -37
  66. metadata +110 -96
  67. data/CHANGELOG.md +0 -14
  68. data/HOW-TO.md +0 -275
  69. data/README.md +0 -39
  70. data/lib/annotation_security/version.rb +0 -10
@@ -1,189 +1,189 @@
1
- require 'optparse'
2
- require 'fileutils'
3
-
4
- # = lib/annotation_security/exec.rb
5
- # This file is borrowed heavily from HAML
6
- #
7
-
8
- module AnnotationSecurity
9
- module Exec # :nodoc:
10
-
11
- # An abstract class that encapsulates the executable
12
- # code for all executables.
13
- class Generic # :nodoc:
14
-
15
- # Parsed options
16
- def options
17
- @options ||= {}
18
- end
19
-
20
- # @param args [Array<String>] The command-line arguments
21
- def initialize(args)
22
- @args = args
23
- end
24
-
25
- # Parses the command-line arguments and runs the executable.
26
- # Calls `Kernel#exit` at the end, so it never returns.
27
- def parse!
28
- begin
29
- @opts = OptionParser.new(&method(:set_opts))
30
- @opts.parse!(@args)
31
-
32
- process_result
33
-
34
- options
35
- rescue Exception => e
36
- raise e if e.is_a?(SystemExit) || options[:trace]
37
-
38
- $stderr.puts e.message
39
- exit 1
40
- end
41
- exit 0
42
- end
43
-
44
- # @return [String] A description of the executable
45
- def to_s
46
- @opts.to_s
47
- end
48
-
49
- protected
50
-
51
- # Tells optparse how to parse the arguments
52
- # available for all executables.
53
- #
54
- # This is meant to be overridden by subclasses
55
- # so they can add their own options.
56
- #
57
- # @param opts [OptionParser]
58
- def set_opts(opts)
59
- opts.on("--force", "Force command execution, override assets without asking") do
60
- options[:force] = true
61
- end
62
-
63
- opts.on("--trace", "Shows full stack trace in case of errors") do
64
- options[:trace] = true
65
- end
66
-
67
- opts.on_tail("-?", "-h", "--help", "Show this message") do
68
- puts opts
69
- exit
70
- end
71
-
72
- opts.on_tail("-v", "--version", "Print version") do
73
- puts("AnnotationSecurity 0.01")
74
- exit
75
- end
76
- end
77
-
78
- # Processes the options set by the command-line arguments.
79
- #
80
- # This is meant to be overridden by subclasses
81
- # so they can run their respective programs.
82
- def process_result; end
83
- end
84
-
85
- # An abstrac class that encapsulates the code
86
- # specific to executables.
87
- class RailsInstaller < Generic # :nodoc:
88
-
89
- ASSET_FILES = %w{
90
- config/initializers/annotation_security.rb
91
- config/security/relations.rb
92
- config/security/rights.yml
93
- app/helpers/annotation_security_helper.rb
94
- vendor/plugins/annotation_security/init.rb
95
- }
96
-
97
- # @param args [Array<String>] The command-line arguments
98
- def initialize(args)
99
- super
100
- @name = "annosec"
101
- end
102
-
103
- protected
104
-
105
- # Tells optparse how to parse the arguments.
106
- #
107
- # This is meant to be overridden by subclasses
108
- # so they can add their own options.
109
- #
110
- # @param opts [OptionParser]
111
- def set_opts(opts)
112
- opts.banner = <<END
113
- Usage: #{@name.downcase} [options]
114
-
115
- Description:
116
- Installs the AnnotationSecurity layer into a rails app
117
-
118
- Options:
119
- END
120
-
121
- opts.on('--rails RAILS_DIR', "Install AnnotationSecurity layer from the Gem to a Rails project") do |dir|
122
- options[:rails_dir] = dir
123
- end
124
-
125
- super
126
- end
127
-
128
- # Processes the options set by the command-line arguments.
129
- # In particular, sets `@options[:for_engine][:filename]` to the input filename
130
- # and requires the appropriate file.
131
- #
132
- # This is meant to be overridden by subclasses
133
- # so they can run their respective programs.
134
- def process_result
135
-
136
- unless options[:rails_dir]
137
- puts @opts
138
- exit
139
- end
140
-
141
- options[:cur_dir] = File.dirname(__FILE__)
142
- options[:assets_dir] = File.join(options[:cur_dir], '..', '..', 'assets')
143
-
144
- assert_exists('config')
145
- assert_exists('vendor')
146
- assert_exists('app/helpers')
147
-
148
- ASSET_FILES.each { |f| install_file(f) }
149
-
150
- puts "AnnotationSecurity plugin added to #{options[:rails_dir]}"
151
- exit
152
- end
153
-
154
- private
155
-
156
- def assert_exists(dir)
157
- dir = File.join(options[:rails_dir], dir)
158
- unless File.exists?(dir)
159
- if options[:force]
160
- puts "Creating #{dir}"
161
- FileUtils.mkdir_p dir
162
- else
163
- puts "Directory #{dir} does not exist"
164
- exit
165
- end
166
- end
167
- end
168
-
169
- def install_file(f)
170
- orign = File.join(options[:assets_dir], f)
171
- dest = File.join(options[:rails_dir], f)
172
-
173
- if File.exists?(dest) && !options[:force]
174
- print "File #{dest} already exists, overwrite [y/N]? "
175
- return if gets !~ /y/i
176
- end
177
-
178
- dir = File.dirname(dest)
179
- unless File.exists?(dir)
180
- puts "Creating #{dir}"
181
- FileUtils.mkdir_p(dir)
182
- end
183
-
184
- FileUtils.install(orign, dest)
185
- puts "Installed #{dest}"
186
- end
187
- end
188
- end
1
+ require 'optparse'
2
+ require 'fileutils'
3
+
4
+ # = lib/annotation_security/exec.rb
5
+ # This file is borrowed heavily from HAML
6
+ #
7
+
8
+ module AnnotationSecurity
9
+ module Exec # :nodoc:
10
+
11
+ # An abstract class that encapsulates the executable
12
+ # code for all executables.
13
+ class Generic # :nodoc:
14
+
15
+ # Parsed options
16
+ def options
17
+ @options ||= {}
18
+ end
19
+
20
+ # @param args [Array<String>] The command-line arguments
21
+ def initialize(args)
22
+ @args = args
23
+ end
24
+
25
+ # Parses the command-line arguments and runs the executable.
26
+ # Calls `Kernel#exit` at the end, so it never returns.
27
+ def parse!
28
+ begin
29
+ @opts = OptionParser.new(&method(:set_opts))
30
+ @opts.parse!(@args)
31
+
32
+ process_result
33
+
34
+ options
35
+ rescue Exception => e
36
+ raise e if e.is_a?(SystemExit) || options[:trace]
37
+
38
+ $stderr.puts e.message
39
+ exit 1
40
+ end
41
+ exit 0
42
+ end
43
+
44
+ # @return [String] A description of the executable
45
+ def to_s
46
+ @opts.to_s
47
+ end
48
+
49
+ protected
50
+
51
+ # Tells optparse how to parse the arguments
52
+ # available for all executables.
53
+ #
54
+ # This is meant to be overridden by subclasses
55
+ # so they can add their own options.
56
+ #
57
+ # @param opts [OptionParser]
58
+ def set_opts(opts)
59
+ opts.on("--force", "Force command execution, override assets without asking") do
60
+ options[:force] = true
61
+ end
62
+
63
+ opts.on("--trace", "Shows full stack trace in case of errors") do
64
+ options[:trace] = true
65
+ end
66
+
67
+ opts.on_tail("-?", "-h", "--help", "Show this message") do
68
+ puts opts
69
+ exit
70
+ end
71
+
72
+ opts.on_tail("-v", "--version", "Print version") do
73
+ puts("AnnotationSecurity 1.0.3")
74
+ exit
75
+ end
76
+ end
77
+
78
+ # Processes the options set by the command-line arguments.
79
+ #
80
+ # This is meant to be overridden by subclasses
81
+ # so they can run their respective programs.
82
+ def process_result; end
83
+ end
84
+
85
+ # An abstrac class that encapsulates the code
86
+ # specific to executables.
87
+ class RailsInstaller < Generic # :nodoc:
88
+
89
+ ASSET_FILES = %w{
90
+ config/initializers/annotation_security.rb
91
+ config/security/relations.rb
92
+ config/security/rights.yml
93
+ app/helpers/annotation_security_helper.rb
94
+ vendor/plugins/annotation_security/init.rb
95
+ }
96
+
97
+ # @param args [Array<String>] The command-line arguments
98
+ def initialize(args)
99
+ super
100
+ @name = "annosec"
101
+ end
102
+
103
+ protected
104
+
105
+ # Tells optparse how to parse the arguments.
106
+ #
107
+ # This is meant to be overridden by subclasses
108
+ # so they can add their own options.
109
+ #
110
+ # @param opts [OptionParser]
111
+ def set_opts(opts)
112
+ opts.banner = <<END
113
+ Usage: #{@name.downcase} [options]
114
+
115
+ Description:
116
+ Installs the AnnotationSecurity layer into a rails app
117
+
118
+ Options:
119
+ END
120
+
121
+ opts.on('--rails RAILS_DIR', "Install AnnotationSecurity layer from the Gem to a Rails project") do |dir|
122
+ options[:rails_dir] = dir
123
+ end
124
+
125
+ super
126
+ end
127
+
128
+ # Processes the options set by the command-line arguments.
129
+ # In particular, sets `@options[:for_engine][:filename]` to the input filename
130
+ # and requires the appropriate file.
131
+ #
132
+ # This is meant to be overridden by subclasses
133
+ # so they can run their respective programs.
134
+ def process_result
135
+
136
+ unless options[:rails_dir]
137
+ puts @opts
138
+ exit
139
+ end
140
+
141
+ options[:cur_dir] = File.dirname(__FILE__)
142
+ options[:assets_dir] = File.join(options[:cur_dir], '..', '..', 'assets')
143
+
144
+ assert_exists('config')
145
+ assert_exists('vendor')
146
+ assert_exists('app/helpers')
147
+
148
+ ASSET_FILES.each { |f| install_file(f) }
149
+
150
+ puts "AnnotationSecurity plugin added to #{options[:rails_dir]}"
151
+ exit
152
+ end
153
+
154
+ private
155
+
156
+ def assert_exists(dir)
157
+ dir = File.join(options[:rails_dir], dir)
158
+ unless File.exists?(dir)
159
+ if options[:force]
160
+ puts "Creating #{dir}"
161
+ FileUtils.mkdir_p dir
162
+ else
163
+ puts "Directory #{dir} does not exist"
164
+ exit
165
+ end
166
+ end
167
+ end
168
+
169
+ def install_file(f)
170
+ orign = File.join(options[:assets_dir], f)
171
+ dest = File.join(options[:rails_dir], f)
172
+
173
+ if File.exists?(dest) && !options[:force]
174
+ print "File #{dest} already exists, overwrite [y/N]? "
175
+ return if gets !~ /y/i
176
+ end
177
+
178
+ dir = File.dirname(dest)
179
+ unless File.exists?(dir)
180
+ puts "Creating #{dir}"
181
+ FileUtils.mkdir_p(dir)
182
+ end
183
+
184
+ FileUtils.install(orign, dest)
185
+ puts "Installed #{dest}"
186
+ end
187
+ end
188
+ end
189
189
  end
@@ -1,215 +1,215 @@
1
- #
2
- # = lib/annotation_security/includes/helper.rb
3
- #
4
-
5
- # = AnnotationSecurity::Helper
6
- #
7
- # This module adds some useful helper methods to your templates.
8
- #
9
- module AnnotationSecurity::Helper
10
-
11
- # Returns true if the operation defined by +policy_args+ is allowed.
12
- #
13
- # The following calls to #allowed? are possible:
14
- #
15
- # allowed? :show, :resource, @resource
16
- # # => true if the current user has the right to show @resource,
17
- # # which belongs to the :resource resource-class
18
- #
19
- # In case of model objects or other classes which implement a #resource_type
20
- # method the the second argument may be ommited
21
- #
22
- # allowed? :show, @resource
23
- # # equivalent to the above call if @resource.resource_type == :resource
24
- #
25
- # A policy description used as a controller annotation may also be used
26
- # to check a right
27
- #
28
- # allowed? "show resource", @resource
29
- # # => true if the current user has the right "show resource" for @resource
30
- #
31
- # A policy may also be applied without an object representing the context:
32
- #
33
- # allowed? :show, :resource
34
- # # => true if the current may show resources.
35
- #
36
- # This will only check system and pretest rules. The result +true+ does not
37
- # mean that the user may show all resources. However, a +false+ indicates
38
- # that the user is not allowed to show any resources.
39
- #
40
- # If the resource class is omitted as well, only rules defined for all
41
- # resources can be tested. See RelationLoader#all_resources for details.
42
- #
43
- # allowed? :administrate
44
- # # => true if the user is allowed to administrate all resources.
45
- #
46
- # See SecurityContext#allowed?.
47
- #
48
- def allowed?(*args)
49
- SecurityContext.allowed?(*args)
50
- end
51
-
52
- alias a? allowed?
53
-
54
- # Equivalent to allowed?; is? is provided for better readability.
55
- #
56
- # allowed? :logged_in
57
- # vs
58
- # is? :logged_in
59
- #
60
- def is?(*args)
61
- SecurityContext.is?(*args)
62
- end
63
-
64
- # Checks whether the user is allowed to access the action.
65
- #
66
- # Expects arguments like #link_to_if_allowed, just without name and block.
67
- #
68
- # Returns true if the action is allowed.
69
- #
70
- def action_allowed?(options, objects=nil, params=nil, html_options=nil)
71
-
72
- options, objects, params, html_options =
73
- parse_allow_action_args(options, objects, params, html_options)
74
-
75
- controller = params.delete :controller
76
- action = params.delete :action
77
- SecurityContext.allow_action?(controller, action, objects, params)
78
- end
79
-
80
- # Returns a link tag with the specified name to the specified resource if
81
- # the user is allowed to access it. See #link_to_unless and
82
- # SecurityContext#action_allowed? for more documentation.
83
- #
84
- # There are two ways of using #link_to_if_allowed
85
- #
86
- # === As #link_to with alternative
87
- # (or as #link_to_unless without explicit condition)
88
- # link_to_if_allowed(name, options={}, html_options=nil) { 'alternative' }
89
- # +options+ either is a hash, like
90
- # { :controller => :comments, :action => edit, :id => @comment }
91
- # a string, like
92
- # "comments/1/edit"
93
- # or
94
- # edit_comment_path(@comment)
95
- # or a single resource object.
96
- #
97
- # Notice that when providing a string, controller, action and parameters will
98
- # be parsed. After that, the resource types of the parameters are *guessed*,
99
- # the resources are retrieved and the rules of the action are evaluated.
100
- #
101
- # The block will be evaluated if the action is not allowed,
102
- # like in #link_to_unless.
103
- #
104
- # === As #link_to with alternative and explicit objects
105
- # link_to_if_allowed(name, options={}, objects=[], params={}, html_options=nil) { 'alternative' }
106
- # In this case, controller and action will be derived from +options+ unless
107
- # they are specified in +params+.
108
- # All items in +objects+ and all remaining items in +params+ will be used
109
- # for evaluating the rules of the action.
110
- #
111
- # If you want to specify +html_options+, provide at least an empty hash
112
- # for +params+.
113
- #
114
- # Unlike to #link_to, you can also provide a symbol as +options+ value.
115
- # In this case, the target url will be determined by sending symbol as
116
- # message, providing +objects+ and +params+ as arguments, e.g.
117
- # link_to_if_allowed("Show comment", :comment_path, [@article, @comment], {:details => true})
118
- # will call
119
- # comment_path(@article, @comment, {:details => true})
120
- #
121
- # === Examples
122
- # <%= link_to_if_allowed("Show", @course) { } %>
123
- # <%= link_to_if_allowed("New", new_course_path) { "You may not create a new course." } %>
124
- #
125
- # These two are equivalent, however, the second approach is more efficient:
126
- # <%= link_to_if_allowed("Edit", edit_course_path(@course)) { } %>
127
- # <%= link_to_if_allowed("Edit", :edit_course_path, @course) { } %>
128
- #
129
- # The HTML-options are taken into account when choosing the action.
130
- # <%= link_to_if_allowed("Delete", @course, {:method => :delete}) { } %>
131
- #
132
- # You can also define all values explicitly
133
- # <%= link_to_if_allowed("Edit comment", "articles/1/comments/5/edit", [@comment], {:article => @comment.article, :action => :edit, :controller => :comments}) { } %>
134
- #
135
- # === Parameters
136
- # - +name+ Text of the link
137
- # - +options+
138
- # - +objects+
139
- # - +params+
140
- # - +html_options+
141
- #
142
- def link_to_if_allowed(name, options, objects=nil, params=nil, html_options=nil, &block)
143
-
144
- options, objects, params, html_options =
145
- parse_allow_action_args(options, objects, params, html_options)
146
-
147
- controller = params.delete :controller
148
- action = params.delete :action
149
- allowed = SecurityContext.allow_action?(controller, action, objects, params)
150
-
151
- link_to_if(allowed, name, options, html_options, &block)
152
- end
153
-
154
- alias link_if_a link_to_if_allowed
155
-
156
- private
157
-
158
- def parse_allow_action_args(*args)
159
- if args.second && !(args.second.is_a? Hash)
160
- # objects and params are specified
161
- options, objects, params, html_options = args
162
- objects = [objects] unless objects.is_a? Array
163
- params ||= {}
164
- html_options ||= {}
165
- if options.is_a? Symbol
166
- # options is a symbol, send the message to get the link path
167
- path_args = objects + [params]
168
- options = send(options, *path_args)
169
- end
170
- else
171
- # retrieve objects and params from options
172
- options = args.first
173
- html_options = args.second || {}
174
- objects = [] # everything will be in the params
175
- if options.is_a? Hash
176
- params = options.dup
177
- else
178
- params = parse_action_params(options, html_options)
179
- end
180
- end
181
-
182
- unless params[:controller] && params[:action]
183
- # if controller and action are not given, parse from options
184
- params = parse_controller_action(options, params, html_options)
185
- end
186
-
187
- [options, objects, params, html_options]
188
- end
189
-
190
- # uses options and html_options to retrieve controller and action,
191
- # adds these values to params hash
192
- def parse_controller_action(options, params, html_options)
193
- path_info = get_path_info(options, html_options)
194
- params[:controller] ||= path_info[:controller]
195
- params[:action] ||= path_info[:action]
196
- params
197
- end
198
-
199
- # uses options and html_options to retrieve controller, action
200
- # and params
201
- def parse_action_params(options, html_options)
202
- get_path_info(options, html_options)
203
- end
204
-
205
- def get_path_info(options, html_options)
206
- if options.is_a? String
207
- path = options
208
- else
209
- path = url_for(options)
210
- end
211
- env = { :method => (html_options[:method] || :get ) }
212
- ActionController::Routing::Routes.recognize_path(path, env)
213
- end
214
-
215
- end
1
+ #
2
+ # = lib/annotation_security/includes/helper.rb
3
+ #
4
+
5
+ # = AnnotationSecurity::Helper
6
+ #
7
+ # This module adds some useful helper methods to your templates.
8
+ #
9
+ module AnnotationSecurity::Helper
10
+
11
+ # Returns true if the operation defined by +policy_args+ is allowed.
12
+ #
13
+ # The following calls to #allowed? are possible:
14
+ #
15
+ # allowed? :show, :resource, @resource
16
+ # # => true if the current user has the right to show @resource,
17
+ # # which belongs to the :resource resource-class
18
+ #
19
+ # In case of model objects or other classes which implement a #resource_type
20
+ # method the the second argument may be ommited
21
+ #
22
+ # allowed? :show, @resource
23
+ # # equivalent to the above call if @resource.resource_type == :resource
24
+ #
25
+ # A policy description used as a controller annotation may also be used
26
+ # to check a right
27
+ #
28
+ # allowed? "show resource", @resource
29
+ # # => true if the current user has the right "show resource" for @resource
30
+ #
31
+ # A policy may also be applied without an object representing the context:
32
+ #
33
+ # allowed? :show, :resource
34
+ # # => true if the current may show resources.
35
+ #
36
+ # This will only check system and pretest rules. The result +true+ does not
37
+ # mean that the user may show all resources. However, a +false+ indicates
38
+ # that the user is not allowed to show any resources.
39
+ #
40
+ # If the resource class is omitted as well, only rules defined for all
41
+ # resources can be tested. See RelationLoader#all_resources for details.
42
+ #
43
+ # allowed? :administrate
44
+ # # => true if the user is allowed to administrate all resources.
45
+ #
46
+ # See SecurityContext#allowed?.
47
+ #
48
+ def allowed?(*args)
49
+ SecurityContext.allowed?(*args)
50
+ end
51
+
52
+ alias a? allowed?
53
+
54
+ # Equivalent to allowed?; is? is provided for better readability.
55
+ #
56
+ # allowed? :logged_in
57
+ # vs
58
+ # is? :logged_in
59
+ #
60
+ def is?(*args)
61
+ SecurityContext.is?(*args)
62
+ end
63
+
64
+ # Checks whether the user is allowed to access the action.
65
+ #
66
+ # Expects arguments like #link_to_if_allowed, just without name and block.
67
+ #
68
+ # Returns true if the action is allowed.
69
+ #
70
+ def action_allowed?(options, objects=nil, params=nil, html_options=nil)
71
+
72
+ options, objects, params, html_options =
73
+ parse_allow_action_args(options, objects, params, html_options)
74
+
75
+ controller = params.delete :controller
76
+ action = params.delete :action
77
+ SecurityContext.allow_action?(controller, action, objects, params)
78
+ end
79
+
80
+ # Returns a link tag with the specified name to the specified resource if
81
+ # the user is allowed to access it. See #link_to_unless and
82
+ # SecurityContext#action_allowed? for more documentation.
83
+ #
84
+ # There are two ways of using #link_to_if_allowed
85
+ #
86
+ # === As #link_to with alternative
87
+ # (or as #link_to_unless without explicit condition)
88
+ # link_to_if_allowed(name, options={}, html_options=nil) { 'alternative' }
89
+ # +options+ either is a hash, like
90
+ # { :controller => :comments, :action => edit, :id => @comment }
91
+ # a string, like
92
+ # "comments/1/edit"
93
+ # or
94
+ # edit_comment_path(@comment)
95
+ # or a single resource object.
96
+ #
97
+ # Notice that when providing a string, controller, action and parameters will
98
+ # be parsed. After that, the resource types of the parameters are *guessed*,
99
+ # the resources are retrieved and the rules of the action are evaluated.
100
+ #
101
+ # The block will be evaluated if the action is not allowed,
102
+ # like in #link_to_unless.
103
+ #
104
+ # === As #link_to with alternative and explicit objects
105
+ # link_to_if_allowed(name, options={}, objects=[], params={}, html_options=nil) { 'alternative' }
106
+ # In this case, controller and action will be derived from +options+ unless
107
+ # they are specified in +params+.
108
+ # All items in +objects+ and all remaining items in +params+ will be used
109
+ # for evaluating the rules of the action.
110
+ #
111
+ # If you want to specify +html_options+, provide at least an empty hash
112
+ # for +params+.
113
+ #
114
+ # Unlike to #link_to, you can also provide a symbol as +options+ value.
115
+ # In this case, the target url will be determined by sending symbol as
116
+ # message, providing +objects+ and +params+ as arguments, e.g.
117
+ # link_to_if_allowed("Show comment", :comment_path, [@article, @comment], {:details => true})
118
+ # will call
119
+ # comment_path(@article, @comment, {:details => true})
120
+ #
121
+ # === Examples
122
+ # <%= link_to_if_allowed("Show", @course) { } %>
123
+ # <%= link_to_if_allowed("New", new_course_path) { "You may not create a new course." } %>
124
+ #
125
+ # These two are equivalent, however, the second approach is more efficient:
126
+ # <%= link_to_if_allowed("Edit", edit_course_path(@course)) { } %>
127
+ # <%= link_to_if_allowed("Edit", :edit_course_path, @course) { } %>
128
+ #
129
+ # The HTML-options are taken into account when choosing the action.
130
+ # <%= link_to_if_allowed("Delete", @course, {:method => :delete}) { } %>
131
+ #
132
+ # You can also define all values explicitly
133
+ # <%= link_to_if_allowed("Edit comment", "articles/1/comments/5/edit", [@comment], {:article => @comment.article, :action => :edit, :controller => :comments}) { } %>
134
+ #
135
+ # === Parameters
136
+ # - +name+ Text of the link
137
+ # - +options+
138
+ # - +objects+
139
+ # - +params+
140
+ # - +html_options+
141
+ #
142
+ def link_to_if_allowed(name, options, objects=nil, params=nil, html_options=nil, &block)
143
+
144
+ options, objects, params, html_options =
145
+ parse_allow_action_args(options, objects, params, html_options)
146
+
147
+ controller = params.delete :controller
148
+ action = params.delete :action
149
+ allowed = SecurityContext.allow_action?(controller, action, objects, params)
150
+
151
+ link_to_if(allowed, name, options, html_options, &block)
152
+ end
153
+
154
+ alias link_if_a link_to_if_allowed
155
+
156
+ private
157
+
158
+ def parse_allow_action_args(*args)
159
+ if args.second && !(args.second.is_a? Hash)
160
+ # objects and params are specified
161
+ options, objects, params, html_options = args
162
+ objects = [objects] unless objects.is_a? Array
163
+ params ||= {}
164
+ html_options ||= {}
165
+ if options.is_a? Symbol
166
+ # options is a symbol, send the message to get the link path
167
+ path_args = objects + [params]
168
+ options = send(options, *path_args)
169
+ end
170
+ else
171
+ # retrieve objects and params from options
172
+ options = args.first
173
+ html_options = args.second || {}
174
+ objects = [] # everything will be in the params
175
+ if options.is_a? Hash
176
+ params = options.dup
177
+ else
178
+ params = parse_action_params(options, html_options)
179
+ end
180
+ end
181
+
182
+ unless params[:controller] && params[:action]
183
+ # if controller and action are not given, parse from options
184
+ params = parse_controller_action(options, params, html_options)
185
+ end
186
+
187
+ [options, objects, params, html_options]
188
+ end
189
+
190
+ # uses options and html_options to retrieve controller and action,
191
+ # adds these values to params hash
192
+ def parse_controller_action(options, params, html_options)
193
+ path_info = get_path_info(options, html_options)
194
+ params[:controller] ||= path_info[:controller]
195
+ params[:action] ||= path_info[:action]
196
+ params
197
+ end
198
+
199
+ # uses options and html_options to retrieve controller, action
200
+ # and params
201
+ def parse_action_params(options, html_options)
202
+ get_path_info(options, html_options)
203
+ end
204
+
205
+ def get_path_info(options, html_options)
206
+ if options.is_a? String
207
+ path = options
208
+ else
209
+ path = url_for(options)
210
+ end
211
+ env = { :method => (html_options[:method] || :get ) }
212
+ ActionController::Routing::Routes.recognize_path(path, env)
213
+ end
214
+
215
+ end