Enforcing Price Plans with Defense in Depth
17/03/2010
In my my controllers I am restricting access to features based on the users assigned price plan, which is an integer, mappable to a label such as 'Gold', 'Silver', 'Bronze'.
As well as providing autherisation at the controller layer, where it belongs, I also wish to enforce this at the model layer. This gives me defense in depth and will also alert me to holes in the autherisation at the controller level where testing dare not tread.
I have a YAML file which has a list of methods (the key) and minimum price plan (the value). At the top of my model I specify which methods I want to protect. At the bottom of model I then alias each method and check the YAML file to make sure the price plan is sufficient, if not I raise an exception, otherwise I call the original method.
class Account RestrictedMethods = %w(enable_logging backup) def enable_logging # do something end def backup # do something end def allow?(action) YAML.load(File.join(RAILS_ROOT, 'config', 'price_plan.yml'))[action.to_s].to_i >= price_plan end RestrictedMethods.each do |method_name| str_id = "__#{method_name}__hooked__" unless private_instance_methods.include?(str_id) alias_method str_id, method_name private str_id define_method method_name do |*args| raise PricePlanRestrictedException unless allow? method_name __send__ str_id, *args # Invoke backup end end end end
The YAML file, price_plan.yml, looks like this:
backup: 5 enable_logging: 10
This was largely inspired by the following post: http://cheind.blogspot.com/2008/12/method-hooks-in-ruby.html