I'm using the CanCan gem in my Rails app and want to check if the current request is a protected resource in my application.
So for example I have the following:
class AdminController < ApplicationController
load_and_authorize_resource
end
How can I check if the request is protected by CanCan?
I can access the controller and action via params. I can't use the standard can? and cannot? helpers as they will check if the current_user has permission rather than if the action itself has a protection on it.
So for example:
class ApplicationController < ActionController::Base
before_action :check_protected
def check_protected
if can? params[:action].to_sym, Object.const_get(params[:controller].classify)
# resource is protected
else
# resource is not protected
end
end
end
^^ This won't work because it will always say false when no current_user or if the user doesn't have permission. I need to check if the resource itself is protected with CanCan.
If I had these examples:
class PostsController < AdminController
def index
end
end
class HomeController < ApplicationController
def index
end
end
The index for PostsController should be identifiable as protected, and the index for HomeController as unprotected.
CanCan uses CanCan::ControllerResource#skip? method to determine whether it should authorize resource or not. So I guess you may rely on it as follows:
def check_protected
if CanCan::ControllerResource.new(self).skip?(:authorize)
# resource is not protected
else
# resource is protected
end
end
I've tried it in my sandbox and it worked for me
Related
Pundit works well, if action has resources like:
class Admin::PagesController << ApplicationController
def index
#pages = Page.all
end
end
How to authorise method without any resources in action?
class Admin::DashboardController << ApplicationController
def index
end
end
I hav file policies/admin/dashboard_policy.rb
class Admin::DashboardPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
end
This file was generated by command:
rails g pundit:policy Admin/Dashboard
File views/admin/index.html.slim has only static text. Nothing more.
How to authorise action without any resources?
Regards
Sssebaaa
To authorize without a scope or model instance call authorize with a symbol or array of symbols (when namespaced):
class Admin::DashboardController << ApplicationController
def index
authorize [:admin, :dashboard]
end
end
This will call the #index? method on the policy class:
class Admin::DashboardPolicy < ApplicationPolicy
def index?
user.admin?
end
end
You can also remove the scope completely from your policy.
If you don't have any callbacks checking that the policy is scoped, as pundit doc suggests, like
class ApplictationController < ActionController::Base
include Pundit
after_action :verify_policy_scoped, only: :index
end
You don't have anything to do.
However if you do have a callback, you can just skip it in your controller action like this:
class Admin::DashboardController << ApplicationController
skip_after_action :verify_policy_scoped, only: [:index]
def index
end
end
I'm using Devise + ominauth2 to allow users to sign in via different services. If they sign in they get a slightly better experience but if not life still goes on. However I don't see a way with Devise to have a page that allows both authenticated and unauthenticated access.
For example:
class SomeController < ApplicationController
before_action :authenticate_user!
def some_action
end
end
In the above controller, some_action is only accessible if the user authenticates. But what I actually want is something more like:
class SomeController < ApplicationController
def some_action
loadSessionFromCookieIfExists
end
end
Then, I can use user_signed_in? in my views. But this doesn't work because authenticate_user! will redirect them elsewhere. Unless there is an authenticate_if_possible method?
So what I have now, which is really gross is:
class SomeController < ApplicationController
before_action :authenticate_user!
skip_before_action :authenticate_user!, :only=>[:no_auth]
def action
do_stuff
return
end
def no_auth
do_stuff
return
end
private
def do_stuff
render :template => "action"
end
end
I should add, that omniauth2 doesn't support rememberable out of the gate so it's possible this is all supposed to work just fine and the problem is with omniauth2.
I am learning to use Pundit for authorization. But the way I see it is authorization for resources not pages. I want a user to be redirected to a unauthorized page if he/she is not authorized to visit the page using pundit.
For e.g.
class OnlyAdminCanVisitController < ApplicationController
before_filter :admin_authenticate
Stops a non-admin role user.
Also, I want to take care of made up scenarios like following(Considering there are 4 roles as Admin,Manager,Employee,Outsider. The design below is obiviously bad)
class AdminManagerCanVisitController < ApplicationController
before_filter :admin_or_manager_authenticate
class AdminEmployeeCanVisitController < ApplicationController
before_filter :admin_or_employee_authenticate
class AdminOutsiderCanVisitController < ApplicationController
before_filter :admin_or_outsider_authenticate
class AdminManagerEmployeeCanVisitController < ApplicationController
before_filter :admin_or_manager_employee_authenticate
I have 4 roles and would like to write pundit policies for these controllers which allows any combination of authorizations.
Let me know if pundit is designed to tackle this issue.
Thanks
There is not much difference between pages and resources actually. So you can solve your problem by rescuing a denied Authorization from your application_controller.rb :
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
protected
def admin_authenticate
authorize current_user, :admin?
end
private
def user_not_authorized(exception)
# Redirect to whatever page you want if not authorized
end
end
You then need to define your policy. I generally create an admin? method in application_policy.rb (https://github.com/elabs/pundit#policies) so it is spread on my other policies as well :
class ApplicationPolicy
def admin?
# Logic to ensure the user is an admin
end
end
class UserPolicy < ApplicationPolicy
end
Then in your other controllers, just do as you did :
class OnlyAdminCanVisitController < ApplicationController
before_action :admin_authenticate
end
I have this view called Intranet where only authenticated "devise clients" can access.
class IntranetController < ApplicationController
before_action :authenticate_client!
def index
end
end
On the other side, I also have other "devise admin", this devise admin requires to access the same view. How can I handle this situation?
Try this:
class IntranetController < ApplicationController
before_action :authenticate_all!
def index
end
def authenticate_all!
if admin_signed_in?
true
else
authenticate_client!
end
end
end
I'd like to control the permit method with something like this
class SomethingController < ApplicationController
permit :somerole
end
where ':somerole' is a field in the database linked to a controller and an action. Something that an user with priviledge can administer and change.
Some Idea?
this is just for example i have
class Admin::AdminController < ApplicationController
before_filter :login_required
before_filter :only_moderator_and_above
layout 'admin'
def only_moderator_and_above
unless current_user.has_admin_access?
flash[:notice] = CustomMessages.admin_permission_alert
redirect_to '/'
end
end
end