I'm new with the Pundit gem and I'm stuck on something.
I try to show a list with multiple records but I always get the error AuthorizationNotPreformedError.
What do I do wrong?
Controller:
def planning
#plans = Order.all
authorize #plans
end
Policy:
def planning?
user.present?
end
Planning.html.haml
%h1 test
When I add this: after_action :verify_policy_scoped, :only => planning, if: verify_policy_scoped?
I get the PolicyScopingNotPerformedError.
From the README:
Pundit tracks whether you have called authorize anywhere in your controller action. Pundit also adds a method to your controllers called verify_authorized
...
Pundit also adds verify_policy_scoped to your controller. This will raise an exception similar to verify_authorized. However, it tracks if policy_scope is used instead of authorize.
You have mixed the two methods up. You've called authorize, but are checking whether policy_scope was called.
policy_scope is typically used for collections of record (such as your example, or more typically index actions), whereas authorize is typically used for individual records (such as show/edit/update/destroy actions).
In your case, however, what you've got currently doesn't necessarily warrant a policy at all - all you're checking is whether the user is signed in!
If you require a user to be signed in, but they are not, then your application should respond with a 401 error, not 403. You may do this with, for example, before_action :authorize in your controller. (It depends how you have implemented authorization -- see the documentation on whatever library you're using, e.g. devise).
Related
Is it possible to access controller parameters when defining abilities in ability.rb?
I have an event and users that can participate in or create that event. It seems like I could create a different controller action for every possible scenario, e.g. a user signs himself up for an event or a creator deletes someone from the event. However I think it would be a lot easier to read to have less actions and be able to define abilities based on what parameters are being passed in from the client.
Answer
#chumakoff has some good info down below that helped explain how CanCanCan is working. I decided to authorize these actions by default in ability.rb, and then raise an error, e.g. raise CanCan::AccessDenied.new("You cannot delete someone else from this event"), in the controller if I detect incorrect user/event parameter IDs being sent in.
If I understand correctly, you are using cancan's authorize_resource or load_and_authorize_resource controller helper that calculates user abilities based on controller actions names.
But it's not obligatory to use this helper for all actions. You can skip it for actions having complex ability logic and check abilities manually.
For example:
class ParticipationsController < ApplicationController
authorize_resource except: :create # skiping `authorize_resource` for `create` action
# ...
def create
if creator_adds_someone_to_event?
authorize! :add_to, #event
end
if user_signs_up_for_event?
authorize! :sign_up_for, #event
end
# ...
end
So, you can check many different abilities in the same controller action. Just disable default cancancan's behaviour for the action.
Yes there is a debugging tool Named as " pry" . Use that it would help u out. Just use binding.pry wherever u want to check the value of parameters in the code and the console will stop executing at that moment so u can check the value of the parameters.
I am trying to allow access to the log-in/sign-up page for a admin user only from my computer or any other way that lets me only see the web page for an admin sing-up-log-in.
Or what do typical web applications do to restrict access to the public towards a certain web page? If there is a bets-practice way, I would like to implement that.
I currently have Devise installed.
You can use the authenticate_user! Devise helper, adding it as callback within the needed controller and specifying the methods you want to control.
For instance if you have a Post model, then adding the authenticate_user! in the PostController it'll ask the user to be logged to have access to the methods in that specific controller:
class PostsController < ApplicationController
before_action :authenticate_user!
If you want to restrict just some specific methods then you can play with only and/or except.
See: Devise will create some helpers to use inside your controllers
and views. To set up a controller with user authentication, just add
this before_action (assuming your devise model is 'User')
Devise - Controller filters and helpers
According to your comment then you can create a method in the ApplicationController in order to restrict all of your controllers and methods.
This way you can define an array of addresses, and if the remote_ip coming from the request is in the array then you give access, if isn't then perform any other action:
ApplicationController < ActionController::Base
before_action :protect
private
def protect
addresses = ['127.0.0.1', ...]
if addresses.include?(request.remote_ip)
# give access
else
# restrict access
end
end
end
But if you need something more sophisticated then you'd have to see on your Nginx or Apache, whatever you're using to deploy your project.
I normally restrict webpage access through controller methods. My recent use case was going to a webpage only when payment was successful but redirecting when it was not, if any body issued a get request for that page directly, it would result in 404.
In your case, there can be multiple option for setup.
You can use cookies to see users credentials using Action Dispatcher
Use Devise for users and then you can fix a certain role to a user through adding a new migration and assign roles yourself after registering or let them choose.
I will expect you followed Devise route. In the controller action check for current user's role.
If User Not signed in (using current_user == nil)
redirect to home page and then return
else
if
its admin you go ahead and use the success page as partial and let them see the page using `<%= render 'pages/mypage'%>` and use return to end
else
just redirect back to home page with a notice "Don't try this".
redirect_to root_path, notice: 'Don't try this' and then use
return to end
end
(Just for fun, to see how many times a user did this wrong action, you can also have a table which stores current_user and number_of_wrong_attempt, and store their email whenever they try to go that page without permission before redirect in controller. After that you can email them with a background rake task which checks for a certain false attempt threshold that: "Hey! Your registration is being removed because you are doing unprohibited actions")
Situation: rails 3.2 app with a demo period, after which users must start paying for the service.
Question: If a user does not add a payment method, or does not choose a payment plan, what is the recommended way of restricting user access to the 'paid' part of the web app?
I need something that sorts users as follows:
if user.admin? || user.in_demo || user.has_all_payment_data
# carry on
elsif user.should_add_payment_method
# send them to add payment method page
elsif user.should_choose_plan
# send them to add plan
else
# redirect to home page or whatever
end
I've started off with a before_filter on the application controller that checks the payment status of the user on every request and redirects them accordingly (skipping this in places like the homepage/profile editing etc.), but I'm thinking there must be a better way, as it's rapidly getting too complicated and it just feels wrong having all that complexity in the application controller. I've been looking at user roles libraries like cancan but I can't find anything that fits.
There is a post by Jonas Nicklas (creator of Capybara and CarrierWave) in which he explains in some detail how to take a simpler approach than CanCan's. His approach is based on an additional plain Ruby class for each model you want to create authorization rules for.
Simple authorization in Ruby on Rails apps (Elabs blog)
They have offloaded that solution into a gem named Pundit, but it really seems simple enough to be able to implement from scratch.
Pundit gem (GitHub)
I would suggest a before_filter in the application controller, then using skip_filter in individual controllers to bypass it for actions that non-paid users can access, e.g:
class ApplicationController < ActionController::Base
before_filter :check_payment
...
end
class UserController < ApplicationController
skip_filter :check_payment, :only => [:login, :logout, ...]
...
end
This keeps the access contained to the relevant controllers, rather than needing an increasingly large :except => ... on the filter itself.
I am about to implement authorization for my Rails app.
I know I can use Cancan, but in my case all the roles I have are: user and admin. I see that people always secure the models with roles (like cancan does).
My take on authorization would be to just add:
before_filter :redirect_if_not_admin :only => :create, ...
and then:
def redirect_if_not_admin
if !#user.is_admin?
redirect_to :root
end
in all my controllers and the actions I want to restrict from being accessed.
Q1. Is this in any way less secure than restricting the access to the models' attributes?
Q2. Is there a way for someone to bypass the before_filter and the redirect and access my controller code?
thanks in advance
Sure -- just add the before_filter and your redirect handler method in application_controller.rb. As long as your user model has a method "is_admin?" this will work fine.
It is as secure as any other method (in fact, it is the method used in most cases like this). The only way to bypass the before filter would be if your code allowed it. Indeed, there are sometimes cases where you want a specific controller method to be allowed, in which case you can override the application_controller.rb filters in specific controllers using the :except or :only conditions.
I have installed and implemented the plugin restful_authentication from technoweenie.
My application is, for the most part, intended to be open, except for the case of say, writing a review.
So I have a reviews_controller.rb, but the only time I want to care whether the user is logged in or not is when they are submitting a specific action add_review.
add_review is an action on the vendor_controller.rb because the form is displayed on vendors/show. Then it redirects back to vendor/show to show the update (thinking of changing this to ajax).
If the person is NOT logged in, I want it to redirect to the login/signup page.
The instructions for restful_authentication show applying the include AuthenticatedSystem at the controller level, but I don't want to be authenticating with other actions in that controller.
How do I do this?
Include the AuthenticatedSystem module in the controller you need it for (or ApplicationController if you need it for more than one), and then use the before_filter applied only to the actions you want, e.g.:
class VendorsController < ApplicationController
include AuthenticatedSystem
before_filter :login_required, :only => [:add_review]
end
I implore you to use the restful create action rather than your own add_review action. Read more about restful routing at: http://guides.rubyonrails.org/routing.html