How to use Devise/CanCan to protect mounted Engine resources?
Asked Answered
K

1

8

I have an engine mounted to my main app and I want to protect certain controllers and actions within that engine.

The engine is mounted with:

mount SomeEngine::Engine => '/some_engine'

Devise/CanCan is working with the rest of the main app's controllers and actions, but letting things run without anything else produces this error:

This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check.

So I open up the engine controllers from the main app using the decorator approach and add:

load_and_authorize_resource

Then I get this error:

No route matches {:action=>"new", :controller=>"devise/sessions"}

I can get things working using the following, but it's clunky when I try to implement roles:

authenticate :administrator do
  mount SomeEngine::Engine => '/some_engine'
end

By clunky I mean I'll have to reproduce the above block of code in the routes.rb file for each role that has access to the engine...unless there's another way to use authenticate with roles that I don't know about???

I'd like to use the normal Devise/CanCan authorization/authentication approach in the controller if possible. But I think "no route match" error occurs because the engine does not know how to get to the main app's Devise controllers. But how do I get around this from the main app?

To throw one more issue into the mix...there is one specific controller/action in the engine that I do want to make public to all users. Thus far I've just added this before the authenticate block of code in the routes.rb file.

match '/some_engine' => 'some_engine/some_controller#public_action'

It works...but this line with the block in the routes.rb seems like I'm doing something wrong. And it doesn't allow me to implement roles nicely.

Kalliekallista answered 31/8, 2012 at 3:19 Comment(0)
G
8

You can inherit application controller for use devise and cancan from main app.

module SomeEngine
  class ApplicationController < ::ApplicationController    
    before_filter :merge_abilities

    private

    def merge_abilities
      current_ability.merge(SomeEngine::Ability.new(current_user))                                                     
    end
  end
end                                                                                                                     

After this you can create abilities for engine by create own.

module SomeEngine
  class Ability
    include ::CanCan::Ability

    def initialize(user)
      return if user.nil?

      can :manage, SomeModel
    end
  end
end

SomeModel (SomeEngine::SomeModel) is model at SomeEngine engine.

At resource controllers you must specify class name of resource.

load_and_authorize_resource class: SomeEngine::SomeModel

And do not forgot change route helper to main_app.MAIN_APP_PATHS at main application layout if you want to use it at engine.

Gluey answered 1/9, 2012 at 9:29 Comment(1)
Wow, I didn't know you can merge the abilities. I ended up following the pattern used in the Forem and Spree gems. Your pattern is pretty much what they're doing but with the added merging of abilities. Thanks!Kalliekallista

© 2022 - 2024 — McMap. All rights reserved.