Rails 4 - Json rendering without authentication - ruby-on-rails

I am new to RoR. So far in my project, I worked with Devise gem for authentication. All work's fine. But I want to get json unauthentic access for API on index. I tried little bit for that.
In my Application Controller.
class ApplicationCotroller < Action Controller::Base
protect_from_forgery_with :exception, :null_session => true
before_action :authenticate_user!, except: :index
end
This is my Product Controller index.
class ProductsController < ApplicationController
skip_before_action :authenticate_user!, only: :index, if: -> { request.format.json? }
before_action :set_product, only: [:show, :edit, :update, :destroy]
def index
#products = Product.all
respond_to do |format|
format.html
format.json { render json: #products}
end
end
When I request to get json
http://localhost:3000/products.json
It works fine. even this will load also the UI index page.
I need only to check if it is JSON request comes.
How to achieve this?

Try this:
class ProductsController < ApplicationController
skip_before_action :authenticate_user!, if: :skip_authenticate_user
def index
...
end
protected
def skip_authenticate_user
action_name=='index' && (request.format.json? || request.format.xml?)
end
end

Try adding skip_before_action in your ProductsController:
class ProductsController < ApplicationController
skip_before_action :authenticate_user!, only: :index
def index
...
end
end
This will ensure that the authentication_user! method is skipped for ProductsController index action.

Related

What is the best way to redirect a user to login screen from 2 or more different controllers

I have this in my application_controller
class ApplicationController < ActionController::Base
before_action :login_required, :only => 'users/login'
protect_from_forgery with: :exception
protected
def login_required
return true if User.find_by_id(session[:user_id])
access_denied
return false
end
def access_denied
flash[:error] = 'Oops. You need to login before you can view that page.'
redirect_to users_login_path
end
end
I want to use the login_required for each controller def method
Is there a better way instead of this?
class UsersController < ApplicationController
before_action :set_user, :login_required, :only => 'users/login'
#before_action only: [:show, :edit, :update, :destroy, :new]
def index
login_required
#users = User.all
end
def new
login_required
#user = User.new
end
end
Is there a better way to include login_required for all controllers methods since before_action doesn't seem to work?
I don't know the motivation of your logic, so I'll just focus on how you can solve this particular problem.
You can do something like this:
In your application controller:
class ApplicationController < ActionController::Base
before_action :login_required
private
def login_required
current_params = params["controller"] + "/" + params["action"]
if current_params == "users/new" or current_params == "users/index"
return true if User.find(session[:user_id])
access_denied
return false
end
end
def access_denied
flash[:error] = 'Oops. You need to login before you can view that page.'
redirect_to users_login_path
end
end
The login_required method will just run only on users controller's index and new action, for the rest, it'll just ignore. Also you can just use User.find() and no need to use User.find_by_id()
Now, in your users_controller.rb, you don't need to mention anything about login_required, everything will happen already in application_controller before coming here.
class UsersController < ApplicationController
before_action :set_user, :only => 'users/login'
#before_action only: [:show, :edit, :update, :destroy, :new]
def index
#users = User.all
end
def new
#user = User.new
end
end
Firstly, I'm going to suggest that you use devise for authentication, it's a lot more secure and should deal with this for you.
As for your problem, you should be able to specify the before_action like this:
before_action :set_user, :login_required, only: [:new]
Which you can put in your UserController. However if you want this globally, just put it in the ApplicationController, without the only: key.
If you want to require login for all pages except /users/login, then you almost have it right except you are specifying only: when you should be using except::
class ApplicationController < ActionController::Base
before_action :login_required, except: 'users/login'
...
end
This configuration will be applied to all sub-classes of ApplicationController as well.

What's the "rails way" to access a resource in a controller's before_action

I'm using Pundit to authorize actions in my controllers. My first try was to authorize the model in an after_action hoook:
class CompaniesController < InheritedResources::Base
after_action :authorize_company, except: :index
def authorize_company
authorize #company
end
This let me use the default controller actions which define #company so I wouldn't hit the database twice. But, this is bad for destructive actions because it's going to not authorize the action after I've already messed up the database.
So, I've changed to using a before_action hook:
class CompaniesController < InheritedResources::Base
before_action :authorize_company, except: :index
def authorize_company
#company = Company.find(params.require(:id))
authorize #company
end
Now, I'm not allowing unauthorized people to delete resources, etc... but I'm hitting the database twice. Is there anyway to access #company without hitting the database twice?
Since your asking for the "rails way" this is how you would set this up in "plain old rails" without InheritedResources.
class CompaniesController < ApplicationController
before_action :authorize_company, except: [:new, :index]
def new
#company = authorize(Company.new)
end
def index
#companies = policy_scope(Company)
end
# ...
private
def authorize_company
#company = authorize(Company.find(params[:id]))
end
end
If you really want to use callbacks you would do it like so:
class CompaniesController < ApplicationController
before_action :authorize_company, except: [:new, :index]
before_action :authorize_companies, only: [:index]
before_action :build_company, only: [:new]
# ...
private
def authorize_company
#company = authorize(Company.find(params[:id]))
end
def authorize_companies
#companies = policy_scope(Company)
end
def build_companies
#company = authorize(Company.new)
end
end
Yeah you could write a single callback method with three code branches but this has lower cyclic complexity and each method does a single job.
Turns out rails controllers have a resource if the model exists and build_resource for actions like new.
class CompaniesController < InheritedResources::Base
before_action :authorize_company, except: :index
private
def authorize_company
authorize resource
rescue ActiveRecord::RecordNotFound
authorize build_resource
end
end

How do you create a before action for one specific page in rails?

I have two controllers: pages_controller and charges_controller
pages_controller just has:
class PagesController < ApplicationController
def show
render template: "pages/#{params[:page]}"
end
end
charges_controller has basic stripe info to accept charges
my routes.rb has a root set to my home page and:
get "/:page" => "pages#show"
I have about 8 html files in views/pages/
I want one and only one of those pages, "products.html.erb" to have a before_action authenticate user!
If I add a before action to the pages controller, it affects every page. Need some tips, new developer.
You can create a custom private method which verifies the value for the params[:page] param, and depending if this is the one which you want or not to restrict then apply the devise authenticate_user before filter only in the show method, something like:
class PagesController < ApplicationController
before_action :custom_authenticate_user!, only: :show
# remains equal
def show
render template: "pages/#{params[:page]}"
end
private
# only if params[:page] equal 'bla' then use the authenticate_user!
def custom_authenticate_user!
authenticate_user! if params[:page] == 'bla'
end
Also this is an easier way, just to use if and the only option in order to check the params and make it work, without having to create a new method:
class PagesController < ApplicationController
before_action :authenticate_user!, only: :show, if: -> { params[:page] == 'bla' }
You can single out the action that renders "products.html.erb" using before_action. If it is only rendered in your show action, it would look like:
before_action :authenticate_user, only: [:show]
You can add as many routes as you want in there like:
before_action :authenticate_user, only: [:show, :create, :destroy]
Here are the docs for it.
class PagesController < ApplicationController
def show
if "#{params[:page]}" == "product"
your action here
end
render template: "pages/#{params[:page]}"
end
end
before_action inside controller usually for "same code" between new/show/edit/update, since you just need special code for one page I think you just need if command before render it
add before_action with only: option to pass an array of actions you want to apply the filter. There is another option you can use skip: to pass an array of actions if you want that filter to apply to all the actions except the action names you want to skip
class PagesController < ApplicationController
before_action :authenticate_user!, only: [:show]
def show
render template: "pages/#{params[:page]}"
end
end

How to properly protect SQL injection in rails controller

I have a controller and I'm wondering if Rails has already parsed the params and prevented SQL injection or not. Here's what my controller looks like:
class V1::JobsController < ApplicationController
include ActionController::HttpAuthentication::Token::ControllerMethods
include ActionController::Serialization
before_action :set_job, only: [:show, :update, :destroy]
before_action :authorize_api_key, only: [:create]
# GET /jobs
def index
status = (params[:status]) ? params[:status] : 'pending'
#jobs = Job.where(publisher_id: params[:publisher_id], status: status)
render json: #jobs
end
...
end
Is this safe as is?
Yes, your code is safe.
For example don't use something like this:
Project.where("name = '#{params[:name]}'") # DON'T USE
For more information

Rails skip before action doesn't work

i've some problem with the skip_before action:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :require_login
before_action :inc_cookies
def inc_cookies
if cookies[:token] != nil
#name = cookies[:name]
#surname = cookies[:surname]
#user_roomate = cookies[:roomate]
end
end
def require_login
if cookies[:token] == nil
puts "No token"
redirect_to '/'
end
end
end
and my other controller:
class UsersController < ApplicationController
skip_before_action :require_login, :except => [:landing, :connect, :create]
end
I don't know why, but when I'm on the root (the :landing action from UsersController), Rails try to pass in the require_login...
I've misundertood something with this filter, or do I something wrong?
Thanks for any help!
This sounds normal to me - you've asked rails to skip your before action, except if the action is :landing, :connect or :create whereas it sounds as though you want the opposite. If you want those 3 actions not to execute the require_login then you should be doing
skip_before_action :require_login, :only => [:landing, :connect, :create]

Resources