I am using Trestle Admin, this is my route:
trestle_path /admin Trestle::Engine
When a user that is not an admin visits the /admin
route, I would like CanCanCan to handle it like it handles all of the other unauthorized requests in my app.
The issue though is that I can't figure out how to specify that ability in my ability.rb
or I can't figure out where to add an authorize
statement to.
When I visit /admin
in my app, this is what my log looks like:
Started GET "/admin" for ::1 at 2019-03-31 01:10:01 -0500
Processing by Trestle::DashboardController#index as HTML
Redirected to http://localhost:3000/admin/login
Filter chain halted as :require_authenticated_user rendered or redirected
Completed 302 Found in 13ms (ActiveRecord: 0.0ms)
So all that happens is that it redirects to /admin/login
which is how the Trestle engine handles it.
But I would like for CanCanCan to hijack that and handle it like it handles all other unauthorized requests throughout my application via the rule in my application_controller.rb
:
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.json { head :forbidden, content_type: 'text/html' }
format.html { redirect_to main_app.root_url, alert: exception.message }
format.js { head :forbidden, content_type: 'text/html' }
end
end
But, given that it isn't a Model or a controller I defined, I am not sure what to specify in my ability.rb
.
I have tried the following, all to no avail:
if user.has_role? :admin
can :manage, :all
else
cannot :read, :trestle
end
or:
if user.has_role? :admin
can :manage, :all
else
cannot :read, :admin
end
Is it possible for me to do what I am trying to do?
You can use a hack with route constraints:
class CanCanConstraint
def initialize(action, resource)
@resource, @action = resource, action
end
def matches?(request)
# this will differ depending on your auth solution, for devise/warden:
current_user = request.env['warden'].user
current_user && Ability.new(current_user).can?(@action, @resource) || false
end
end
mount Trestle::Engine, at: '/admin', constraints: CanCanConstraint.new(:edit, :trestle)
match "/admin/*glob", to: "some_controller_when#trestle_not_authorized"
This way /admin
only leads to trestle if user is authorized and has permission, otherwise first route will not match and you can handle request any way you like, for example with catch-all second route.
Depending on how you mount trestle - you may need to disable it's automount (config.automount = false
in corresponding initializer)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With