diff --git a/app/controllers/administrate/application_controller.rb b/app/controllers/administrate/application_controller.rb index 63a5674b33..114060da5e 100644 --- a/app/controllers/administrate/application_controller.rb +++ b/app/controllers/administrate/application_controller.rb @@ -105,10 +105,27 @@ def nav_link_state(resource) resource_name.to_s.pluralize == underscore_resource ? :active : :inactive end - helper_method :valid_action? - def valid_action?(name, resource = resource_class) - routes.include?([resource.to_s.underscore.pluralize, name.to_s]) + # Whether the named action route exists for the resource class. + # + # @param resource [Class, String, Symbol] A class of resources, or the name + # of a class of resources. + # @param action_name [String, Symbol] The name of an action that might be + # possible to perform on a resource or resource class. + # @return [Boolean] `true` if a route exists for the resource class and the + # action. `false` otherwise. + def existing_action?(resource, action_name) + routes.include?([resource.to_s.underscore.pluralize, action_name.to_s]) + end + helper_method :existing_action? + + # @deprecated Use {#existing_action} instead. Note that, in + # {#existing_action}, the order of parameters is reversed and + # there is no default value for the `resource` parameter. + def valid_action?(action_name, resource = resource_class) + Administrate.warn_of_deprecated_authorization_method(__method__) + existing_action?(resource, action_name) end + helper_method :valid_action? def routes @routes ||= Namespace.new(namespace).routes.to_set @@ -226,9 +243,26 @@ def show_search_bar? ).any? { |_name, attribute| attribute.searchable? } end - def show_action?(_action, _resource) + # Whether the current user is authorized to perform the named action on the + # resource. + # + # @param _resource [ActiveRecord::Base, Class, String, Symbol] The + # temptative target of the action, or the name of its class. + # @param _action_name [String, Symbol] The name of an action that might be + # possible to perform on a resource or resource class. + # @return [Boolean] `true` if the current user is authorized to perform the + # action on the resource. `false` otherwise. + def authorized_action?(_resource, _action_name) true end + helper_method :authorized_action? + + # @deprecated Use {#authorized_action} instead. Note that the order of + # parameters is reversed in {#authorized_action}. + def show_action?(action, resource) + Administrate.warn_of_deprecated_authorization_method(__method__) + authorized_action?(resource, action) + end helper_method :show_action? def new_resource @@ -237,7 +271,14 @@ def new_resource helper_method :new_resource def authorize_resource(resource) - resource + if authorized_action?(resource, action_name) + resource + else + raise Administrate::NotAuthorizedError.new( + action: action_name, + resource: resource, + ) + end end def paginate_resources(resources) diff --git a/app/controllers/concerns/administrate/punditize.rb b/app/controllers/concerns/administrate/punditize.rb index d49de39468..f655cc16c0 100644 --- a/app/controllers/concerns/administrate/punditize.rb +++ b/app/controllers/concerns/administrate/punditize.rb @@ -5,6 +5,8 @@ module Punditize include Pundit::Authorization included do + private + def scoped_resource policy_scope_admin super end @@ -13,7 +15,7 @@ def authorize_resource(resource) authorize resource end - def show_action?(action, resource) + def authorized_action?(resource, action) Pundit.policy!(pundit_user, resource).send("#{action}?".to_sym) end end diff --git a/app/helpers/administrate/application_helper.rb b/app/helpers/administrate/application_helper.rb index efc9378c8d..b314abd898 100644 --- a/app/helpers/administrate/application_helper.rb +++ b/app/helpers/administrate/application_helper.rb @@ -25,6 +25,28 @@ def model_from_resource(resource_name) dashboard.try(:model) || resource_name.to_sym end + # Unification of + # {Administrate::ApplicationController#existing_action? existing_action?} + # and + # {Administrate::ApplicationController#authorized_action? + # authorized_action?} + # + # @param target [ActiveRecord::Base, Class, Symbol, String] A resource, + # a class of resources, or the name of a class of resources. + # @param action_name [String, Symbol] The name of an action that might be + # possible to perform on a resource or resource class. + # @return [Boolean] Whether the action both (a) exists for the record class, + # and (b) the current user is authorized to perform it on the record + # instance or class. + def accessible_action?(target, action_name) + target = target.to_sym if target.is_a?(String) + target_class_or_class_name = + target.is_a?(ActiveRecord::Base) ? target.class : target + + existing_action?(target_class_or_class_name, action_name) && + authorized_action?(target, action_name) + end + def display_resource_name(resource_name, opts = {}) dashboard_from_resource(resource_name).resource_name( count: opts[:singular] ? SINGULAR_COUNT : PLURAL_MANY_COUNT, diff --git a/app/views/administrate/application/_collection.html.erb b/app/views/administrate/application/_collection.html.erb index 96b44bdfd8..044a53efdd 100644 --- a/app/views/administrate/application/_collection.html.erb +++ b/app/views/administrate/application/_collection.html.erb @@ -59,13 +59,13 @@ to display a collection of resources in an HTML table.