Skip to content

Commit

Permalink
Fix stffn#204, allow :additional_collection to work with strong params
Browse files Browse the repository at this point in the history
  • Loading branch information
zeiv committed May 29, 2015
1 parent 45e91af commit 2f82d16
Showing 1 changed file with 48 additions and 49 deletions.
97 changes: 48 additions & 49 deletions lib/declarative_authorization/in_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

module Authorization
module AuthorizationInController

def self.included(base) # :nodoc:
base.extend(ClassMethods)
base.hide_action :authorization_engine, :permitted_to?,
:permitted_to!
end

DEFAULT_DENY = false

# If attribute_check is set for filter_access_to, decl_auth_context will try to
# load the appropriate object from the current controller's model with
# the id from params[:id]. If that fails, a 404 Not Found is often the
Expand All @@ -31,12 +31,12 @@ def self.failed_auto_loading_is_not_found= (new_value)
def authorization_engine
@authorization_engine ||= Authorization::Engine.instance
end

# If the current user meets the given privilege, permitted_to? returns true
# and yields to the optional block. The attribute checks that are defined
# in the authorization rules are only evaluated if an object is given
# for context.
#
#
# See examples for Authorization::AuthorizationHelper #permitted_to?
#
# If no object or context is specified, the controller_name is used as
Expand All @@ -50,7 +50,7 @@ def permitted_to? (privilege, object_or_sym = nil, options = {})
false
end
end

# Works similar to the permitted_to? method, but
# throws the authorization exceptions, just like Engine#permit!
def permitted_to! (privilege, object_or_sym = nil, options = {})
Expand All @@ -59,7 +59,7 @@ def permitted_to! (privilege, object_or_sym = nil, options = {})

# While permitted_to? is used for authorization, in some cases
# content should only be shown to some users without being concerned
# with authorization. E.g. to only show the most relevant menu options
# with authorization. E.g. to only show the most relevant menu options
# to a certain group of users. That is what has_role? should be used for.
def has_role? (*roles, &block)
user_roles = authorization_engine.roles_for(current_user)
Expand All @@ -69,8 +69,8 @@ def has_role? (*roles, &block)
yield if result and block_given?
result
end
# Intended to be used where you want to allow users with any single listed role to view

# Intended to be used where you want to allow users with any single listed role to view
# the content in question
def has_any_role?(*roles,&block)
user_roles = authorization_engine.roles_for(current_user)
Expand All @@ -80,7 +80,7 @@ def has_any_role?(*roles,&block)
yield if result and block_given?
result
end

# As has_role? except checks all roles included in the role hierarchy
def has_role_with_hierarchy?(*roles, &block)
user_roles = authorization_engine.roles_with_hierarchy_for(current_user)
Expand All @@ -90,7 +90,7 @@ def has_role_with_hierarchy?(*roles, &block)
yield if result and block_given?
result
end

# As has_any_role? except checks all roles included in the role hierarchy
def has_any_role_with_hierarchy?(*roles, &block)
user_roles = authorization_engine.roles_with_hierarchy_for(current_user)
Expand All @@ -100,7 +100,7 @@ def has_any_role_with_hierarchy?(*roles, &block)
yield if result and block_given?
result
end

protected
def filter_access_filter # :nodoc:
permissions = self.class.all_filter_access_permissions
Expand Down Expand Up @@ -210,16 +210,16 @@ module ClassMethods
# filter_access_to :all
# ...
# end
#
#
# The default is to allow access unconditionally if no rule matches.
# Thus, including the +filter_access_to+ :+all+ statement is a good
# idea, implementing a default-deny policy.
#
#
# When the access is denied, the method +permission_denied+ is called
# on the current controller, if defined. Else, a simple "you are not
# allowed" string is output. Log.info is given more information on the
# reasons of denial.
#
#
# def permission_denied
# flash[:error] = 'Sorry, you are not allowed to the requested page.'
# respond_to do |format|
Expand All @@ -228,7 +228,7 @@ module ClassMethods
# format.js { head :unauthorized }
# end
# end
#
#
# By default, required privileges are inferred from the action name and
# the controller name. Thus, in UserController :+edit+ requires
# :+edit+ +users+. To specify required privilege, use the option :+require+
Expand All @@ -252,25 +252,25 @@ module ClassMethods
# end
# NOTE: +before_filters+ need to be defined before the first
# +filter_access_to+ call.
#
#
# For further customization, a custom filter expression may be formulated
# in a block, which is then evaluated in the context of the controller
# on a matching request. That is, for checking two objects, use the
# on a matching request. That is, for checking two objects, use the
# following:
# filter_access_to :merge do
# permitted_to!(:update, User.find(params[:original_id])) and
# permitted_to!(:delete, User.find(params[:id]))
# end
# The block should raise a Authorization::AuthorizationError or return
# false if the access is to be denied.
#
#
# Later calls to filter_access_to with overlapping actions overwrite
# previous ones for that action.
#
#
# All options:
# [:+require+]
# [:+require+]
# Privilege required; defaults to action_name
# [:+context+]
# [:+context+]
# The privilege's context, defaults to decl_auth_context, which consists
# of controller_name, prepended by any namespaces
# [:+attribute_check+]
Expand All @@ -283,19 +283,19 @@ module ClassMethods
# * a find on the context model, using +params+[:id] as id value.
# Any of these methods will only be employed if :+attribute_check+
# is enabled.
# [:+model+]
# [:+model+]
# The data model to load a context object from. Defaults to the
# context, singularized.
# [:+load_method+]
# Specify a method by symbol or a Proc object which should be used
# Specify a method by symbol or a Proc object which should be used
# to load the object. Both should return the loaded object.
# If a Proc object is given, e.g. by way of
# +lambda+, it is called in the instance of the controller.
# +lambda+, it is called in the instance of the controller.
# Example demonstrating the default behavior:
# filter_access_to :show, :attribute_check => true,
# :load_method => lambda { User.find(params[:id]) }
#
#

def filter_access_to (*args, &filter_block)
options = args.last.is_a?(Hash) ? args.pop : {}
options = {
Expand All @@ -313,27 +313,27 @@ def filter_access_to (*args, &filter_block)
# prevent setting filter_access_filter multiple times
skip_before_filter :filter_access_filter
before_filter :filter_access_filter

filter_access_permissions.each do |perm|
perm.remove_actions(actions)
end
filter_access_permissions <<
filter_access_permissions <<
ControllerPermission.new(actions, privilege, context,
options[:strong_parameters],
options[:attribute_check],
options[:model],
options[:load_method],
filter_block)
end

# Collecting all the ControllerPermission objects from the controller
# hierarchy. Permissions for actions are overwritten by calls to
# hierarchy. Permissions for actions are overwritten by calls to
# filter_access_to in child controllers with the same action.
def all_filter_access_permissions # :nodoc:
ancestors.inject([]) do |perms, mod|
if mod.respond_to?(:filter_access_permissions, true)
perms +
mod.filter_access_permissions.collect do |p1|
perms +
mod.filter_access_permissions.collect do |p1|
p1.clone.remove_actions(perms.inject(Set.new) {|actions, p2| actions + p2.actions})
end
else
Expand Down Expand Up @@ -393,12 +393,12 @@ def all_filter_access_permissions # :nodoc:
# filter_resource_access :additional_member => { :toggle_open => :update }
# Would add a member action :+toggle_open+ to the default members, such as :+show+.
#
# If :+collection+ is an array of method names filter_resource_access will
# associate a permission with the method that is the same as the method
# name and no attribute checks will be performed unless
# If :+collection+ is an array of method names filter_resource_access will
# associate a permission with the method that is the same as the method
# name and no attribute checks will be performed unless
# :attribute_check => true
# is added in the options.
#
#
# You can override the default object loading by implementing any of the
# following instance methods on the controller. Examples are given for the
# BranchController (with +nested_in+ set to :+companies+):
Expand All @@ -409,7 +409,7 @@ def all_filter_access_permissions # :nodoc:
# [+load_branch+]
# Used for +member+ actions.
# [+load_company+]
# Used for all +new+, +member+, and +collection+ actions if the
# Used for all +new+, +member+, and +collection+ actions if the
# +nested_in+ option is set.
#
# All options:
Expand Down Expand Up @@ -507,7 +507,7 @@ def filter_resource_access(options = {})
collections = actions_from_option(options[:collection]).merge(
actions_from_option(options[:additional_collection]))

no_attribute_check_actions = options[:strong_parameters] ? actions_from_option(options[:collection]).merge(actions_from_option([:create])) : collections
no_attribute_check_actions = options[:strong_parameters] ? collections.merge(actions_from_option([:create])) : collections

options[:no_attribute_check] ||= no_attribute_check_actions.keys unless options[:nested_in]

Expand Down Expand Up @@ -555,7 +555,7 @@ def filter_resource_access(options = {})
controller.send(:new_blank_controller_object,
options[:context] || controller_name, options[:nested_in], options[:strong_parameters], options[:model])
end
end
end
end

load_method = :"load_#{controller_name.singularize}"
Expand Down Expand Up @@ -595,7 +595,7 @@ def decl_auth_context
prefixes = name.split('::')[0..-2].map(&:underscore)
((prefixes + [controller_name]) * '_').to_sym
end

protected
def filter_access_permissions # :nodoc:
unless filter_access_permissions?
Expand All @@ -606,7 +606,7 @@ def filter_access_permissions # :nodoc:
class_variable_set(:@@declarative_authorization_permissions, {}) unless filter_access_permissions?
class_variable_get(:@@declarative_authorization_permissions)[self.name] ||= []
end

def filter_access_permissions? # :nodoc:
class_variable_defined?(:@@declarative_authorization_permissions)
end
Expand All @@ -632,7 +632,7 @@ def actions_from_option (option) # :nodoc:
end
end
end

class ControllerPermission # :nodoc:
attr_reader :actions, :privilege, :context, :attribute_check, :strong_params
def initialize (actions, privilege, context, strong_params, attribute_check = false,
Expand All @@ -647,30 +647,30 @@ def initialize (actions, privilege, context, strong_params, attribute_check = fa
@attribute_check = attribute_check
@strong_params = strong_params
end

def matches? (action_name)
@actions.include?(action_name.to_sym)
end

def permit! (contr)
if @filter_block
return contr.instance_eval(&@filter_block)
end
object = @attribute_check ? load_object(contr) : nil
privilege = @privilege || :"#{contr.action_name}"

contr.authorization_engine.permit!(privilege,
contr.authorization_engine.permit!(privilege,
:user => contr.send(:current_user),
:object => object,
:skip_attribute_test => !@attribute_check,
:context => @context || contr.class.decl_auth_context)
end

def remove_actions (actions)
@actions -= actions
self
end

private

def load_object(contr)
Expand Down Expand Up @@ -701,4 +701,3 @@ def load_object(contr)
end
end
end

0 comments on commit 2f82d16

Please sign in to comment.