declarative_authorization_padrino 0.1.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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,27 @@
1
+ = Declarative Authorization wrapper for Padrino
2
+
3
+ Read https://github.com/stffn/declarative_authorization for everything else but how to secure your controllers.
4
+
5
+ == Securing your controllers
6
+
7
+ Although it's not the best approach and it has to be improved, it'll do the job for now. Suggestions accepted :)
8
+
9
+ Register the module in your app/app.rb
10
+
11
+ register Authorization::Padrino
12
+
13
+ For each action on your controller you'd like to protect add something like this:
14
+
15
+ :protect => [{:action => :read, :resource => :users, :forbidden => "Custom 403 forbidden message"}]
16
+
17
+ Note that :forbidden is optional.
18
+
19
+ get '/', :provides => :json, :protect => [{:action => :read, :resource => :users}] do
20
+ User.with_permissions_to.all.to_json(:include => :roles)
21
+ end
22
+
23
+ And that's pretty much it :)
24
+
25
+ == Demo app
26
+
27
+ https://github.com/dariocravero/declarative_authorization_padrino_demo_app
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+
3
+ require "bundler"
4
+ Bundler.setup
5
+
6
+ require 'rake'
7
+ require 'rake/gempackagetask'
8
+
9
+ gemspec = eval(File.read('declarative_authorization_padrino.gemspec'))
10
+ Rake::GemPackageTask.new(gemspec) do |pkg|
11
+ pkg.gem_spec = gemspec
12
+ end
13
+
14
+ desc "build the gem and release it to rubygems.org"
15
+ task :release => :gem do
16
+ puts "Tagging #{gemspec.version}..."
17
+ system "git tag -a #{gemspec.version} -m 'Tagging #{gemspec.version}'"
18
+ puts "Pushing to Github..."
19
+ system "git push --tags"
20
+ puts "Pushing to rubygems.org..."
21
+ system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
22
+ end
@@ -0,0 +1,22 @@
1
+ require "active_support/core_ext/module/delegation" # for some funny reason I need this here! ActiveRecord might not be pulling it from its dependencies?
2
+
3
+ require File.join("declarative_authorization_padrino", "authorization")
4
+
5
+ # Stub Rails so we don't have to change almost everything in obligation_scope
6
+ module Authorization
7
+ module Rails
8
+ def self.version
9
+ "3"
10
+ end
11
+ end
12
+ end
13
+
14
+ require "declarative_authorization/helper"
15
+ require File.join("declarative_authorization_padrino", "in_model")
16
+ require "declarative_authorization/in_controller"
17
+ require "declarative_authorization/in_model"
18
+ require "declarative_authorization/obligation_scope"
19
+
20
+ ActiveRecord::Base.send :include, Authorization::AuthorizationInModel
21
+
22
+ require File.join("declarative_authorization_padrino", "padrino")
@@ -0,0 +1,27 @@
1
+ module Authorization
2
+ AUTH_DSL_FILES = [Pathname.new(PADRINO_ROOT || '').join("config", "authorization_rules.rb").to_s] unless defined? AUTH_DSL_FILES
3
+
4
+ def self.activate_authorization_rules_browser? # :nodoc:
5
+ ::Padrino.env == :development
6
+ end
7
+
8
+ class Engine
9
+ # Returns the role symbols of the given user.
10
+ def roles_for (user)
11
+ user ||= Authorization.current_user
12
+ raise AuthorizationUsageError, "User object doesn't respond to roles (#{user.inspect})" \
13
+ if !user.respond_to?(:role_symbols) and !user.respond_to?(:roles)
14
+
15
+ ::Padrino.logger.info("The use of user.roles is deprecated. Please add a method " +
16
+ "role_symbols to your User model.") if defined?(::Padrino) and ::Padrino.respond_to?(:logger) and !user.respond_to?(:role_symbols)
17
+
18
+ roles = user.respond_to?(:role_symbols) ? user.role_symbols : user.roles
19
+
20
+ raise AuthorizationUsageError, "User.#{user.respond_to?(:role_symbols) ? 'role_symbols' : 'roles'} " +
21
+ "doesn't return an Array of Symbols (#{roles.inspect})" \
22
+ if !roles.is_a?(Array) or (!roles.empty? and !roles[0].is_a?(Symbol))
23
+
24
+ (roles.empty? ? [Authorization.default_role] : roles)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ # Authorization::AuthorizationInModel
2
+ module Authorization
3
+ module AuthorizationInModel
4
+ def self.included(base) # :nodoc:
5
+ base.module_eval do
6
+ # Activates model security for the current model. Then, CRUD operations
7
+ # are checked against the authorization of the current user. The
8
+ # privileges are :+create+, :+read+, :+update+ and :+delete+ in the
9
+ # context of the model. By default, :+read+ is not checked because of
10
+ # performance impacts, especially with large result sets.
11
+ #
12
+ # class User < ActiveRecord::Base
13
+ # using_access_control
14
+ # end
15
+ #
16
+ # If an operation is not permitted, a Authorization::AuthorizationError
17
+ # is raised.
18
+ #
19
+ # To activate model security on all models, call using_access_control
20
+ # on ActiveRecord::Base
21
+ # ActiveRecord::Base.using_access_control
22
+ #
23
+ # Available options
24
+ # [:+context+] Specify context different from the models table name.
25
+ # [:+include_read+] Also check for :+read+ privilege after find.
26
+ #
27
+ def self.using_access_control (options = {})
28
+ options = {
29
+ :context => nil,
30
+ :include_read => false
31
+ }.merge(options)
32
+
33
+ class_eval do
34
+ [:create, :update, [:destroy, :delete]].each do |action, privilege|
35
+ send(:"before_#{action}") do |object|
36
+ Authorization::Engine.instance.permit!(privilege || action,
37
+ :object => object, :context => options[:context])
38
+ end
39
+ end
40
+
41
+ if options[:include_read]
42
+ # after_find is only called if after_find is implemented
43
+ after_find do |object|
44
+ Authorization::Engine.instance.permit!(:read, :object => object,
45
+ :context => options[:context])
46
+ end
47
+ end
48
+
49
+ def self.using_access_control?
50
+ true
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,341 @@
1
+ module Authorization
2
+ # The +ObligationScope+ class parses any number of obligations into joins and conditions.
3
+ #
4
+ # In +ObligationScope+ parlance, "association paths" are one-dimensional arrays in which each
5
+ # element represents an attribute or association (or "step"), and "leads" to the next step in the
6
+ # association path.
7
+ #
8
+ # Suppose we have this path defined in the context of model Foo:
9
+ # +{ :bar => { :baz => { :foo => { :attr => is { user } } } } }+
10
+ #
11
+ # To parse this path, +ObligationScope+ evaluates each step in the context of the preceding step.
12
+ # The first step is evaluated in the context of the parent scope, the second step is evaluated in
13
+ # the context of the first, and so forth. Every time we encounter a step representing an
14
+ # association, we make note of the fact by storing the path (up to that point), assigning it a
15
+ # table alias intended to match the one that will eventually be chosen by ActiveRecord when
16
+ # executing the +find+ method on the scope.
17
+ #
18
+ # +@table_aliases = {
19
+ # [] => 'foos',
20
+ # [:bar] => 'bars',
21
+ # [:bar, :baz] => 'bazzes',
22
+ # [:bar, :baz, :foo] => 'foos_bazzes' # Alias avoids collisions with 'foos' (already used)
23
+ # }+
24
+ #
25
+ # At the "end" of each path, we expect to find a comparison operation of some kind, generally
26
+ # comparing an attribute of the most recent association with some other value (such as an ID,
27
+ # constant, or array of values). When we encounter a step representing a comparison, we make
28
+ # note of the fact by storing the path (up to that point) and the comparison operation together.
29
+ # (Note that individual obligations' conditions are kept separate, to allow their conditions to
30
+ # be OR'ed together in the generated scope options.)
31
+ #
32
+ # +@obligation_conditions[<obligation>][[:bar, :baz, :foo]] = [
33
+ # [ :attr, :is, <user.id> ]
34
+ # ]+
35
+ #
36
+ # TODO update doc for Relations:
37
+ # After successfully parsing an obligation, all of the stored paths and conditions are converted
38
+ # into scope options (stored in +proxy_options+ as +:joins+ and +:conditions+). The resulting
39
+ # scope may then be used to find all scoped objects for which at least one of the parsed
40
+ # obligations is fully met.
41
+ #
42
+ # +@proxy_options[:joins] = { :bar => { :baz => :foo } }
43
+ # @proxy_options[:conditions] = [ 'foos_bazzes.attr = :foos_bazzes__id_0', { :foos_bazzes__id_0 => 1 } ]+
44
+ #
45
+ class ObligationScope < ActiveRecord::Relation
46
+ def initialize (model, options)
47
+ @finder_options = {}
48
+ super(model, model.table_name)
49
+ end
50
+
51
+ def scope
52
+ self.klass.scoped(@finder_options)
53
+ end
54
+
55
+ # Consumes the given obligation, converting it into scope join and condition options.
56
+ def parse!( obligation )
57
+ @current_obligation = obligation
58
+ @join_table_joins = Set.new
59
+ obligation_conditions[@current_obligation] ||= {}
60
+ follow_path( obligation )
61
+
62
+ rebuild_condition_options!
63
+ rebuild_join_options!
64
+ end
65
+
66
+ protected
67
+
68
+ # Parses the next step in the association path. If it's an association, we advance down the
69
+ # path. Otherwise, it's an attribute, and we need to evaluate it as a comparison operation.
70
+ def follow_path( steps, past_steps = [] )
71
+ if steps.is_a?( Hash )
72
+ steps.each do |step, next_steps|
73
+ path_to_this_point = [past_steps, step].flatten
74
+ reflection = reflection_for( path_to_this_point ) rescue nil
75
+ if reflection
76
+ follow_path( next_steps, path_to_this_point )
77
+ else
78
+ follow_comparison( next_steps, past_steps, step )
79
+ end
80
+ end
81
+ elsif steps.is_a?( Array ) && steps.length == 2
82
+ if reflection_for( past_steps )
83
+ follow_comparison( steps, past_steps, :id )
84
+ else
85
+ follow_comparison( steps, past_steps[0..-2], past_steps[-1] )
86
+ end
87
+ else
88
+ raise "invalid obligation path #{[past_steps, steps].inspect}"
89
+ end
90
+ end
91
+
92
+ def top_level_model
93
+ self.klass
94
+ end
95
+
96
+ def finder_options
97
+ @finder_options
98
+ end
99
+
100
+ # At the end of every association path, we expect to see a comparison of some kind; for
101
+ # example, +:attr => [ :is, :value ]+.
102
+ #
103
+ # This method parses the comparison and creates an obligation condition from it.
104
+ def follow_comparison( steps, past_steps, attribute )
105
+ operator = steps[0]
106
+ value = steps[1..-1]
107
+ value = value[0] if value.length == 1
108
+
109
+ add_obligation_condition_for( past_steps, [attribute, operator, value] )
110
+ end
111
+
112
+ # Adds the given expression to the current obligation's indicated path's conditions.
113
+ #
114
+ # Condition expressions must follow the format +[ <attribute>, <operator>, <value> ]+.
115
+ def add_obligation_condition_for( path, expression )
116
+ raise "invalid expression #{expression.inspect}" unless expression.is_a?( Array ) && expression.length == 3
117
+ add_obligation_join_for( path )
118
+ obligation_conditions[@current_obligation] ||= {}
119
+ ( obligation_conditions[@current_obligation][path] ||= Set.new ) << expression
120
+ end
121
+
122
+ # Adds the given path to the list of obligation joins, if we haven't seen it before.
123
+ def add_obligation_join_for( path )
124
+ map_reflection_for( path ) if reflections[path].nil?
125
+ end
126
+
127
+ # Returns the model associated with the given path.
128
+ def model_for (path)
129
+ reflection = reflection_for(path)
130
+
131
+ if reflection.respond_to?(:proxy_reflection)
132
+ reflection.proxy_reflection.klass
133
+ elsif reflection.respond_to?(:klass)
134
+ reflection.klass
135
+ else
136
+ reflection
137
+ end
138
+ end
139
+
140
+ # Returns the reflection corresponding to the given path.
141
+ def reflection_for(path, for_join_table_only = false)
142
+ @join_table_joins << path if for_join_table_only and !reflections[path]
143
+ reflections[path] ||= map_reflection_for( path )
144
+ end
145
+
146
+ # Returns a proper table alias for the given path. This alias may be used in SQL statements.
147
+ def table_alias_for( path )
148
+ table_aliases[path] ||= map_table_alias_for( path )
149
+ end
150
+
151
+ # Attempts to map a reflection for the given path. Raises if already defined.
152
+ def map_reflection_for( path )
153
+ raise "reflection for #{path.inspect} already exists" unless reflections[path].nil?
154
+
155
+ reflection = path.empty? ? top_level_model : begin
156
+ parent = reflection_for( path[0..-2] )
157
+ if !parent.respond_to?(:proxy_reflection) and parent.respond_to?(:klass)
158
+ parent.klass.reflect_on_association( path.last )
159
+ else
160
+ parent.reflect_on_association( path.last )
161
+ end
162
+ rescue
163
+ parent.reflect_on_association( path.last )
164
+ end
165
+ raise "invalid path #{path.inspect}" if reflection.nil?
166
+
167
+ reflections[path] = reflection
168
+ map_table_alias_for( path ) # Claim a table alias for the path.
169
+
170
+ # Claim alias for join table
171
+ # TODO change how this is checked
172
+ if !reflection.respond_to?(:proxy_reflection) and !reflection.respond_to?(:proxy_scope) and reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
173
+ join_table_path = path[0..-2] + [reflection.options[:through]]
174
+ reflection_for(join_table_path, true)
175
+ end
176
+
177
+ reflection
178
+ end
179
+
180
+ # Attempts to map a table alias for the given path. Raises if already defined.
181
+ def map_table_alias_for( path )
182
+ return "table alias for #{path.inspect} already exists" unless table_aliases[path].nil?
183
+
184
+ reflection = reflection_for( path )
185
+ table_alias = reflection.table_name
186
+ if table_aliases.values.include?( table_alias )
187
+ max_length = reflection.active_record.connection.table_alias_length
188
+ # Rails seems to pluralize reflection names
189
+ table_alias = "#{reflection.name.to_s.pluralize}_#{reflection.active_record.table_name}".to(max_length-1)
190
+ end
191
+ while table_aliases.values.include?( table_alias )
192
+ if table_alias =~ /\w(_\d+?)$/
193
+ table_index = $1.succ
194
+ table_alias = "#{table_alias[0..-(table_index.length+1)]}_#{table_index}"
195
+ else
196
+ table_alias = "#{table_alias[0..(max_length-3)]}_2"
197
+ end
198
+ end
199
+ table_aliases[path] = table_alias
200
+ end
201
+
202
+ # Returns a hash mapping obligations to zero or more condition path sets.
203
+ def obligation_conditions
204
+ @obligation_conditions ||= {}
205
+ end
206
+
207
+ # Returns a hash mapping paths to reflections.
208
+ def reflections
209
+ # lets try to get the order of joins right
210
+ @reflections ||= ActiveSupport::OrderedHash.new
211
+ end
212
+
213
+ # Returns a hash mapping paths to proper table aliases to use in SQL statements.
214
+ def table_aliases
215
+ @table_aliases ||= {}
216
+ end
217
+
218
+ # Parses all of the defined obligation conditions and defines the scope's :conditions option.
219
+ def rebuild_condition_options!
220
+ conds = []
221
+ binds = {}
222
+ used_paths = Set.new
223
+ delete_paths = Set.new
224
+ obligation_conditions.each_with_index do |array, obligation_index|
225
+ obligation, conditions = array
226
+ obligation_conds = []
227
+ conditions.each do |path, expressions|
228
+ model = model_for( path )
229
+ table_alias = table_alias_for(path)
230
+ parent_model = (path.length > 1 ? model_for(path[0..-2]) : top_level_model)
231
+ expressions.each do |expression|
232
+ attribute, operator, value = expression
233
+ # prevent unnecessary joins:
234
+ if attribute == :id and operator == :is and parent_model.columns_hash["#{path.last}_id"]
235
+ attribute_name = :"#{path.last}_id"
236
+ attribute_table_alias = table_alias_for(path[0..-2])
237
+ used_paths << path[0..-2]
238
+ delete_paths << path
239
+ else
240
+ attribute_name = model.columns_hash["#{attribute}_id"] && :"#{attribute}_id" ||
241
+ model.columns_hash[attribute.to_s] && attribute ||
242
+ :id
243
+ attribute_table_alias = table_alias
244
+ used_paths << path
245
+ end
246
+ bindvar = "#{attribute_table_alias}__#{attribute_name}_#{obligation_index}".to_sym
247
+
248
+ sql_attribute = "#{parent_model.connection.quote_table_name(attribute_table_alias)}." +
249
+ "#{parent_model.connection.quote_table_name(attribute_name)}"
250
+ if value.nil? and [:is, :is_not].include?(operator)
251
+ obligation_conds << "#{sql_attribute} IS #{[:contains, :is].include?(operator) ? '' : 'NOT '}NULL"
252
+ else
253
+ attribute_operator = case operator
254
+ when :contains, :is then "= :#{bindvar}"
255
+ when :does_not_contain, :is_not then "<> :#{bindvar}"
256
+ when :is_in, :intersects_with then "IN (:#{bindvar})"
257
+ when :is_not_in then "NOT IN (:#{bindvar})"
258
+ when :lt then "< :#{bindvar}"
259
+ when :lte then "<= :#{bindvar}"
260
+ when :gt then "> :#{bindvar}"
261
+ when :gte then ">= :#{bindvar}"
262
+ else raise AuthorizationUsageError, "Unknown operator: #{operator}"
263
+ end
264
+ obligation_conds << "#{sql_attribute} #{attribute_operator}"
265
+ binds[bindvar] = attribute_value(value)
266
+ end
267
+ end
268
+ end
269
+ obligation_conds << "1=1" if obligation_conds.empty?
270
+ conds << "(#{obligation_conds.join(' AND ')})"
271
+ end
272
+ (delete_paths - used_paths).each {|path| reflections.delete(path)}
273
+
274
+ finder_options[:conditions] = [ conds.join( " OR " ), binds ]
275
+ end
276
+
277
+ def attribute_value (value)
278
+ value.class.respond_to?(:descends_from_active_record?) && value.class.descends_from_active_record? && value.id ||
279
+ value.is_a?(Array) && value[0].class.respond_to?(:descends_from_active_record?) && value[0].class.descends_from_active_record? && value.map( &:id ) ||
280
+ value
281
+ end
282
+
283
+ # Parses all of the defined obligation joins and defines the scope's :joins or :includes option.
284
+ # TODO: Support non-linear association paths. Right now, we just break down the longest path parsed.
285
+ def rebuild_join_options!
286
+ joins = (finder_options[:joins] || []) + (finder_options[:includes] || [])
287
+
288
+ reflections.keys.each do |path|
289
+ next if path.empty? or @join_table_joins.include?(path)
290
+
291
+ existing_join = joins.find do |join|
292
+ existing_path = join_to_path(join)
293
+ min_length = [existing_path.length, path.length].min
294
+ existing_path.first(min_length) == path.first(min_length)
295
+ end
296
+
297
+ if existing_join
298
+ if join_to_path(existing_join).length < path.length
299
+ joins[joins.index(existing_join)] = path_to_join(path)
300
+ end
301
+ else
302
+ joins << path_to_join(path)
303
+ end
304
+ end
305
+
306
+ case obligation_conditions.length
307
+ when 0 then
308
+ # No obligation conditions means we don't have to mess with joins or includes at all.
309
+ when 1 then
310
+ finder_options[:joins] = joins
311
+ finder_options.delete( :include )
312
+ else
313
+ finder_options.delete( :joins )
314
+ finder_options[:include] = joins
315
+ end
316
+ end
317
+
318
+ def path_to_join (path)
319
+ case path.length
320
+ when 0 then nil
321
+ when 1 then path[0]
322
+ else
323
+ hash = { path[-2] => path[-1] }
324
+ path[0..-3].reverse.each do |elem|
325
+ hash = { elem => hash }
326
+ end
327
+ hash
328
+ end
329
+ end
330
+
331
+ def join_to_path (join)
332
+ case join
333
+ when Symbol
334
+ [join]
335
+ when Hash
336
+ [join.keys.first] + join_to_path(join[join.keys.first])
337
+ end
338
+ end
339
+ end
340
+ end
341
+
@@ -0,0 +1,28 @@
1
+ module Authorization
2
+ module Padrino
3
+ def self.registered(app)
4
+ app.extend(Protect)
5
+ app.helpers Authorization::AuthorizationInController
6
+ end
7
+
8
+ module Protect
9
+ def protect(*args)
10
+ condition {
11
+ unless permitted_to? args[0][:action], args[0][:resource]
12
+ halt 403, args[0][:forbidden] || "Can't access this"
13
+ end
14
+ }
15
+ end
16
+ end
17
+
18
+ # Include this module in your helpers if you don't have an auth system that provides them :)
19
+ module CurrentUser
20
+ def current_user
21
+ Authorization.current_user
22
+ end
23
+ def current_user=(user)
24
+ Authorization.current_user=user
25
+ end
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: declarative_authorization_padrino
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.1
6
+ platform: ruby
7
+ authors:
8
+ - Dario Javier Cravero
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-11 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: declarative_authorization
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.2
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ description:
27
+ email: dario@qinnova.com.ar
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - README.rdoc
34
+ files:
35
+ - MIT-LICENSE
36
+ - README.rdoc
37
+ - Rakefile
38
+ - lib/declarative_authorization_padrino.rb
39
+ - lib/declarative_authorization_padrino/authorization.rb
40
+ - lib/declarative_authorization_padrino/in_model.rb
41
+ - lib/declarative_authorization_padrino/obligation_scope.rb
42
+ - lib/declarative_authorization_padrino/padrino.rb
43
+ homepage: http://github.com/dariocravero/declarative_authorization_padrino
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 1.8.6
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.1
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: declarative_authorization_padrino is a Padrino's wrapper around declarative_authorization's Rails plugin for maintainable authorization based on readable authorization rules.
70
+ test_files: []
71
+