annotation_security 1.0.2 → 1.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.
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