Forum: Ruby on Rails Bug in Rails 1.1 implementation of before_filters

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Fc0ee047293c139ca30b36799ab0c34e?d=identicon&s=25 Graham Glass (Guest)
on 2006-04-25 09:13
I just spent a few hours tracking down a bug in Rails 1.1, so I thought
I'd post the issue and a workaround just in case anyone else hits it.

I designed a security enhancement so that controller methods can be
protected by preceding them with a role. The standard Ruby
method_added() callback is used to detect when controller methods are
added, and then I manipulate the controller's before_filter chain
appropriately. My code worked fine in Rails 1.0, and then stopped
working in Rails 1.1.

I eventually tracked down the bug to the new implementation of
ActionController::Filters::before_filters and include_actions, which
cache their results the first time they are called. This caching
approach works fine as long as before_filters and include_actions are
only called *after* all the filters have been registered, but I was
using them *during* the registration process to figure out how to
manipulate the filter chain. Since Rails 1.1 caches their value the
first time they're called, their return value is out of date if more
filters are added.

A workaround is to bypass the cache and use
read_inheritable_attribute("before_filters") instead of before_filters.
Similarly, use read_inheritable_attribute("included_actions") instead of
included_actions. This is bad because now my code is tied to an
implementation detail which would normally be hidden.

Incidentally, the same bug also occurs in after_filters and
excluded_actions.

Cheers,
Graham
E555e7c34196967444a47a96395a23ab?d=identicon&s=25 Stefan Kaes (Guest)
on 2006-04-25 13:56
(Received via mailing list)
Graham Glass wrote:
> I eventually tracked down the bug to the new implementation of
> read_inheritable_attribute("before_filters") instead of before_filters.
> Similarly, use read_inheritable_attribute("included_actions") instead of
> included_actions. This is bad because now my code is tied to an
> implementation detail which would normally be hidden.
>

Incidentally, your previous code relied on an implementation detail as
well: namely, that the values returned by "before_filters" and
"included_actions" were recomputed on each call.

The named methods are usually only called during request processing. In
fact, they are pretty much internal to the filter implementation.
Caching the return values improved performance, sometimes significantly,
so it was decided to cache them.

If you really need to modify the filter chain in your app, use
read_inheritable_attribute("before_filters"), or define your own version
of "before_filters" etc, or simply overwrite the methods with their old
version, by requiring the following code from your environment.rb file:

 class ActionController::Base

  class << self
    # Returns all the before filters for this class and all its
ancestors.
    def before_filters #:nodoc:
      read_inheritable_attribute('before_filters') || []
    end

    # Returns all the after filters for this class and all its
ancestors.
    def after_filters #:nodoc:
      read_inheritable_attribute('after_filters') || []
    end

    # Returns a mapping between filters and the actions that may run
them.
    def included_actions #:nodoc:
      read_inheritable_attribute('included_actions') || {}
    end

    # Returns a mapping between filters and actions that may not run
them.
    def excluded_actions #:nodoc:
      read_inheritable_attribute('excluded_actions') || {}
    end
  end
end

> Incidentally, the same bug also occurs in after_filters and excluded_actions.
>

It's a feature, not a bug.

-- stefan

--
For rails performance tuning, see: http://railsexpress.de/blog
Subscription: http://railsexpress.de/blog/xml/rss20/feed.xml
Fc0ee047293c139ca30b36799ab0c34e?d=identicon&s=25 Graham Glass (Guest)
on 2006-04-25 23:19
Stefan Kaes wrote:
> Graham Glass wrote:
>> I eventually tracked down the bug to the new implementation of
>> read_inheritable_attribute("before_filters") instead of before_filters.
>> Similarly, use read_inheritable_attribute("included_actions") instead of
>> included_actions. This is bad because now my code is tied to an
>> implementation detail which would normally be hidden.
>>
>
> Incidentally, your previous code relied on an implementation detail as
> well: namely, that the values returned by "before_filters" and
> "included_actions" were recomputed on each call.
>
> The named methods are usually only called during request processing. In
> fact, they are pretty much internal to the filter implementation.
> Caching the return values improved performance, sometimes significantly,
> so it was decided to cache them.
>
> If you really need to modify the filter chain in your app, use
> read_inheritable_attribute("before_filters"), or define your own version
> of "before_filters" etc, or simply overwrite the methods with their old
> version, by requiring the following code from your environment.rb file:
>
>  class ActionController::Base
>
>   class << self
>     # Returns all the before filters for this class and all its
> ancestors.
>     def before_filters #:nodoc:
>       read_inheritable_attribute('before_filters') || []
>     end
>
>     # Returns all the after filters for this class and all its
> ancestors.
>     def after_filters #:nodoc:
>       read_inheritable_attribute('after_filters') || []
>     end
>
>     # Returns a mapping between filters and the actions that may run
> them.
>     def included_actions #:nodoc:
>       read_inheritable_attribute('included_actions') || {}
>     end
>
>     # Returns a mapping between filters and actions that may not run
> them.
>     def excluded_actions #:nodoc:
>       read_inheritable_attribute('excluded_actions') || {}
>     end
>   end
> end
>
>> Incidentally, the same bug also occurs in after_filters and excluded_actions.
>>
>
> It's a feature, not a bug.
>
> -- stefan
>
> --
> For rails performance tuning, see: http://railsexpress.de/blog
> Subscription: http://railsexpress.de/blog/xml/rss20/feed.xml

Hi Stefan,

I think it would be good if there were public apis with well-defined
semantics that allowed runtime access to the filter chain. The
approaches that you recommend are not guaranteed to work on future
versions of Rails.

Right now, "before_filters" does not do what its documentation says it
does, so I consider this to be a bug, not a feature.

Cheers,
Graham
This topic is locked and can not be replied to.