restful_authentication applied to only an action within a controller? - ruby-on-rails

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

Related

How do you restrict access to a certain webpage?

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")

Not sure if I am using before_filter/before_action correctly in Rails

I haven't used Rails in some time now, but I am aware Rails comes with things like
application_controller.rb
before_filter :login_with_access_token
helper_method :current_user
From my understanding, before_filter will run the aftersaid method before EVERY action in thus controller. And considering this is the ApplicationController it will run it before every action, since everything inherits from the ApplicationController. And I guess a helper_method is a method that we check often and can be accessed in our views.
I guess the reason I am kinda confused is I don't fully see why we would need helper_methods other than the fact you would want you use them in the views. For example, if I just wanna check if a person model is an adult... what would persuade one from choosing a helper_method vs. action method? Ex.
def is_adult?
person.age > 18
end
Now this is where I am getting confused in my specific problem.
There are certain pages that you can only view if you have a Food object selected. Meaning, my route:
get '/user/:user_id/food/:food_id', to: 'units#display_user_food', as: 'display_user_food'
I proceeded to add a before_filter :user_has_food
def user_has_food
unless UserFood.where(id: params["user_id"]).empty?
true
else
unit_id = params["unit_id"]
false
puts "------------ User does not have food :("
redirect_to "/user/new-food/#{unit_id}"
end
end
But... now the problem is... I have a very thick controller... there are many actions... and if it hits another action, it just fails, and will attempt to redirect me since user_has_food turns out false. Looking at this... it doesn't seem right. What is the ideal and optimal solution to properly address this problem?
I would like to do something with the /app/helpers folder, but I can't seem to find updated tutorials on how to go about using them.
You can call before_filter only for some specific controller action like following
before_filter :user_has_food, only: [:display_user_food]
In rails 5.0 before_filter is deprecated and you should use before_action. And if you have only one action to filtered, you can avoid using array, see the bellow snippet
before_filter :user_has_food, only: :display_user_food
When you use only option in before_filter it will ignored calling while request comes for other action.
Helper method is accessible in any view and controller but action methods are bound to object of that particular class. So something like current_user that doesn't need any object should be a helper method.
Rokibul correctly pointed out about using only: [] syntax to avoid other controller action.
But note that all before_filters syntax are deprecated in Rails 5.0 and will be removed in Rails 5.1, Use before_action instead.

Difference between actions and filters in rails

I am a beginner and I have a really basic question confusing me i.e
What is the difference between actions and filters?
for example we have before_action and after_action in rails 4 but we call them filters, why?
I read somewhere that filters are for controllers and actions are for models, is that it?
OK, so this is how I see things:
1: An action is a method of a controller to which you can route to. For example, your user creation page might be routed to UsersController#new - new is the action in this route.
2: Filters run in respect to controller actions - before, after or around them. These methods can halt the action processing by redirecting or set up common data to every action in the controller.
For example:
before_action :require_logged_out, only: [:new]
def require_logged_out
redirect_to user_path(current_user) if logged_in?
end
So here, require_logged_out is a filter which runs before the new action of the users controller.
3: Lastly I'll mention that in Rails 3.x *_action filters have been defined via *_filter. for example, you'd set a before_filter and not a before_action.
I think that should wrap things up for you regarding actions and filters in rails.
Actions are the rails 4.0 name for filters. They do the exact same thing but before_action is a clearer name for the behavior.
Here is where the name was changed in code.
https://github.com/rails/rails/commit/9d62e04838f01f5589fa50b0baa480d60c815e2c
Here is a link to another discussion on the matter.
Rails 4: before_filter vs. before_action

What are the best practices for authorization?

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.

Rails authorization: Can I just add a before_filter for all actions? without using Cancan

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.

Resources