Search code examples
ruby-on-railsrubyaccess-control

Rails simple access control


I am aware that several gems are made to handle authorization in Rails. But is it really worth it to use these gems for simple access controls ?

I only have a few "roles" in my application, and I feel that a powerful gem would be useless and even slow down the response time.

I have already implemented a solution, but then I took some security classes (:p) and I realized my model was wrong ("Allow by default, then restrict" instead of "Deny by default, then allow").

Now how can I simply implement a "deny by default, allow on specific cases" ?

Basically I'd like to put at the very top of my ApplicationController

class ApplicationController < ApplicationController::Base
  before_filter :deny_access

And at the very top of my other controllers :

class some_controller < ApplicationController
  before_filter :allow_access_to_[entity/user]

These allow_access_to_ before_filters should do something like skip_before_filter

def allow_access_to_[...]
  skip_before_filter(:deny_access) if condition
end

But this doesn't work, because these allow_access before filters are not evaluated before the deny_access before_filter

Any workaround, better solution for this custom implementation of access control ?

EDIT

  • Many non-RESTful actions
  • I need per-action access control
  • undefined method 'skip_before_filter' for #<MyController... why ?
  • My before_filters can get tricky
before_action :find_project, except: [:index, :new, :create]
before_action(except: [:show, :index, :new, :create]) do |c|
   c.restrict_access_to_manager(@project.manager)
end

Solution

  • Rolling your own implementation isn't necessarily bad as long as you're committed to it.

    It won't get tested and maintained by the community so you must be willing to maintain it yourself in the long run, and if it compromises security you need to be really sure of what you're doing and take extra care. If you have that covered and the existing alternatives don't really fit your needs, making your own isn't such a bad idea. And generally it's an incredibly good learning experience.

    I rolled my own with ActionAccess and I couldn't be happier with the results.

    • Locked by default aproach:

      class ApplicationController < ActionController::Base
        lock_access
      
        # ...
      end
      
    • Per-action access control:

      class ArticlesController < ApplicationController
        let :admins, :all
        let :editors, [:index, :show, :edit, :update]
        let :all, [:index, :show]
      
        def index
          # ...
        end
      
        # ...
      end
      
    • Really lighweight implementation.

    I encourage you not to use it but to check out the source code, it has a fare share of comments and should be a good source of inspiration. ControllerAdditions might be a good place to start.

    ActionAccess follows a different approach internally, but you can refactor your answer to mimic it's API with something like this:

    module AccessControl
      extend ActiveSupport::Concern
    
      included do
        before_filter :lock_access
      end
    
      module ClassMethods
        def lock_access
          unless @authorized
            # Redirect user...
          end
        end
    
        def allow_manager_to(actions = [])
          prepend_before_action only: actions do
            @authorized = true if current_user_is_a_manager?
          end
        end
      end
    end
    
    class ApplicationController < ActionController::Base
      include AccessControl  # Locked by default
    
      # ...
    end
    
    class ProjectController < ApplicationController
      allow_managers_to [:edit, :update]  # Per-action access control
    
      # ...
    end
    

    Take this example as pseudo-code, I haven't tested it.

    Hope this helps.