Skip to content

Commit

Permalink
Merge branch 'rails3'
Browse files Browse the repository at this point in the history
  • Loading branch information
stffn committed Mar 4, 2010
2 parents 39e2df9 + b21d8cd commit 322bff9
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 120 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
@@ -1,3 +1,5 @@
* Rails 3 support

* Support shallow nested resources [jjb]

* Allow multiple authorization rules files [kaichen]
Expand Down
2 changes: 1 addition & 1 deletion README.rdoc
Expand Up @@ -379,7 +379,7 @@ Then,

== Providing the Plugin's Requirements
The requirements are
* Rails >= 2.1 and Ruby >= 1.8.6, including 1.9
* Rails >= 2.2, including 3 and Ruby >= 1.8.6, including 1.9
* An authentication mechanism
* A user object returned by Controller#current_user
* An array of role symbols returned by User#role_symbols
Expand Down
3 changes: 1 addition & 2 deletions lib/declarative_authorization/in_controller.rb
Expand Up @@ -274,8 +274,7 @@ def filter_access_to (*args, &filter_block)
context = options[:context]
actions = args.flatten

# collect permits in controller array for use in one before_filter
unless filter_chain.any? {|filter| filter.method == :filter_access_filter}
if all_filter_access_permissions.empty?
before_filter :filter_access_filter
end

Expand Down
23 changes: 13 additions & 10 deletions lib/declarative_authorization/in_model.rb
Expand Up @@ -69,11 +69,12 @@ def self.obligation_scope_for( privileges, options = {} )
}.merge(options)
engine = options[:engine] || Authorization::Engine.instance

scope = ObligationScope.new( options[:model], {} )
obligation_scope = ObligationScope.new( options[:model], {} )
engine.obligations( privileges, :user => options[:user], :context => options[:context] ).each do |obligation|
scope.parse!( obligation )
obligation_scope.parse!( obligation )
end
scope

obligation_scope.scope
end

# Named scope for limiting query results according to the authorization
Expand Down Expand Up @@ -132,15 +133,17 @@ def self.using_access_control (options = {})
:object => object, :context => options[:context])
end
end

# after_find is only called if after_find is implemented
after_find do |object|
Authorization::Engine.instance.permit!(:read, :object => object,
:context => options[:context])
end

if options[:include_read]
def after_find; end
# after_find is only called if after_find is implemented
after_find do |object|
Authorization::Engine.instance.permit!(:read, :object => object,
:context => options[:context])
end

if Rails.version < "3"
def after_find; end
end
end

def self.using_access_control?
Expand Down
2 changes: 1 addition & 1 deletion lib/declarative_authorization/maintenance.rb
Expand Up @@ -78,7 +78,7 @@ def self.usages_by_controller
end
end

actions = controller.public_instance_methods(false) - controller.hidden_actions
actions = controller.public_instance_methods(false) - controller.hidden_actions.to_a
memo[controller] = actions.inject({}) do |actions_memo, action|
action_sym = action.to_sym
actions_memo[action_sym] =
Expand Down
49 changes: 39 additions & 10 deletions lib/declarative_authorization/obligation_scope.rb
Expand Up @@ -33,6 +33,7 @@ module Authorization
# [ :attr, :is, <user.id> ]
# ]+
#
# TODO update doc for Relations:
# After successfully parsing an obligation, all of the stored paths and conditions are converted
# into scope options (stored in +proxy_options+ as +:joins+ and +:conditions+). The resulting
# scope may then be used to find all scoped objects for which at least one of the parsed
Expand All @@ -42,7 +43,20 @@ module Authorization
# @proxy_options[:conditions] = [ 'foos_bazzes.attr = :foos_bazzes__id_0', { :foos_bazzes__id_0 => 1 } ]+
#
class ObligationScope < ActiveRecord::NamedScope::Scope

def initialize (model, options)
@finder_options = {}
super(model, options)
end

def scope
if Rails.version < "3"
self
else
# for Rails < 3: scope, after setting proxy_options
self.klass.scoped(@finder_options)
end
end

# Consumes the given obligation, converting it into scope join and condition options.
def parse!( obligation )
@current_obligation = obligation
Expand Down Expand Up @@ -79,6 +93,18 @@ def follow_path( steps, past_steps = [] )
raise "invalid obligation path #{[past_steps, steps].inspect}"
end
end

def top_level_model
if Rails.version < "3"
@proxy_scope
else
self.klass
end
end

def finder_options
Rails.version < "3" ? @proxy_options : @finder_options
end

# At the end of every association path, we expect to see a comparison of some kind; for
# example, +:attr => [ :is, :value ]+.
Expand Down Expand Up @@ -135,7 +161,7 @@ def table_alias_for( path )
def map_reflection_for( path )
raise "reflection for #{path.inspect} already exists" unless reflections[path].nil?

reflection = path.empty? ? @proxy_scope : begin
reflection = path.empty? ? top_level_model : begin
parent = reflection_for( path[0..-2] )
if !parent.respond_to?(:proxy_reflection) and parent.respond_to?(:klass)
parent.klass.reflect_on_association( path.last )
Expand All @@ -151,6 +177,7 @@ def map_reflection_for( path )
map_table_alias_for( path ) # Claim a table alias for the path.

# Claim alias for join table
# TODO change how this is checked
if !reflection.respond_to?(:proxy_scope) and reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
join_table_path = path[0..-2] + [reflection.options[:through]]
reflection_for(join_table_path, true)
Expand Down Expand Up @@ -209,7 +236,7 @@ def rebuild_condition_options!
conditions.each do |path, expressions|
model = model_for( path )
table_alias = table_alias_for(path)
parent_model = (path.length > 1 ? model_for(path[0..-2]) : @proxy_scope)
parent_model = (path.length > 1 ? model_for(path[0..-2]) : top_level_model)
expressions.each do |expression|
attribute, operator, value = expression
# prevent unnecessary joins:
Expand All @@ -227,7 +254,8 @@ def rebuild_condition_options!
end
bindvar = "#{attribute_table_alias}__#{attribute_name}_#{obligation_index}".to_sym

sql_attribute = "#{connection.quote_table_name(attribute_table_alias)}.#{connection.quote_table_name(attribute_name)}"
sql_attribute = "#{parent_model.connection.quote_table_name(attribute_table_alias)}." +
"#{parent_model.connection.quote_table_name(attribute_name)}"
if value.nil? and [:is, :is_not].include?(operator)
obligation_conds << "#{sql_attribute} IS #{[:contains, :is].include?(operator) ? '' : 'NOT '}NULL"
else
Expand All @@ -247,7 +275,8 @@ def rebuild_condition_options!
conds << "(#{obligation_conds.join(' AND ')})"
end
(delete_paths - used_paths).each {|path| reflections.delete(path)}
@proxy_options[:conditions] = [ conds.join( " OR " ), binds ]

finder_options[:conditions] = [ conds.join( " OR " ), binds ]
end

def attribute_value (value)
Expand All @@ -259,7 +288,7 @@ def attribute_value (value)
# Parses all of the defined obligation joins and defines the scope's :joins or :includes option.
# TODO: Support non-linear association paths. Right now, we just break down the longest path parsed.
def rebuild_join_options!
joins = (@proxy_options[:joins] || []) + (@proxy_options[:includes] || [])
joins = (finder_options[:joins] || []) + (finder_options[:includes] || [])

reflections.keys.each do |path|
next if path.empty? or @join_table_joins.include?(path)
Expand All @@ -283,11 +312,11 @@ def rebuild_join_options!
when 0 then
# No obligation conditions means we don't have to mess with joins or includes at all.
when 1 then
@proxy_options[:joins] = joins
@proxy_options.delete( :include )
finder_options[:joins] = joins
finder_options.delete( :include )
else
@proxy_options.delete( :joins )
@proxy_options[:include] = joins
finder_options.delete( :joins )
finder_options[:include] = joins
end
end

Expand Down
5 changes: 3 additions & 2 deletions test/controller_test.rb
Expand Up @@ -220,7 +220,8 @@ def test_filter_access_with_object_load
authorization do
role :test_role do
has_permission_on :load_mock_objects, :to => [:show, :edit] do
if_attribute :id => is {"1"}
if_attribute :id => 1
if_attribute :id => "1"
end
end
end
Expand Down Expand Up @@ -372,7 +373,7 @@ class CommonChild1Controller < CommonController
end
class CommonChild2Controller < CommonController
filter_access_to :delete
define_action_methods :show
define_action_methods :show, :delete
end
class HierachicalControllerTest < ActionController::TestCase
tests CommonChild2Controller
Expand Down

0 comments on commit 322bff9

Please sign in to comment.