Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #204, allow :additional_collection to work with strong params #207

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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