I want to disable CSRF security for one controller. My ApplicationController looks like this:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
protect_from_forgery with: :exception
And controller where i want to skip:
class HelpdeskInboxController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
prepend_before_filter :require_no_authentication
include Mandrill::Rails::WebHookProcessor
authenticate_with_mandrill_keys! MANDRILL_CONFIG['WEBHOOKS']
And it isn't working i've got error
Can't verify CSRF token authenticity
When mandrill sends me an email.
I believe skip_before_action :verify_authenticity_token will only work with rails 4. Try skip_before_filter instead.
Related
I am implementing rspec test for destroy action, the concept is that signed in user can only destroy his own posts, and cannot destroy posts creates by other users.
The `new_post` is created by a user named `creator`, and another user named `user1` signed in and try to delete the `new_post`, it should not be able to delete it, because of the ` before_action :authenticate_user!, only: %i[create destroy]` in Posts controller
Posts controller.
class PostsController < ApplicationController
before_action :set_post, only: %i[show edit update destroy]
before_action :current_user, only: %i[create destroy]
before_action :authenticate_user!, only: %i[create destroy]
.
.
.
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:content, :picture)
end
end
users controller spec
require 'rails_helper'
RSpec.describe PostsController, type: :controller do
context 'DELETE #destroy' do
let(:user1) {User.create!(name:"John", email:"john#mail.com", password:"password")}
let(:creator) { User.create!(name: "creator", email: "creaor#gmail.com", password: "password") }
let(:new_post){creator.posts.create!(content: "Neque porro quisquam est qui dolorem ipsum")}
it 'A user cannot delete a post created by other user' do
sign_in user1
p (new_post)
expect { delete :destroy, params: { id: new_post.id } }.to change(Post, :count).by(0)
end
end
end
Failures:
1) PostsController DELETE #destroy A user cannot delete a post created by other user
Failure/Error: expect { delete :destroy, params: { id: new_post.id } }.to change(Post, :count).by(0)
expected `Post.count` to have changed by 0, but was changed by -1
I believe you need to add an authorization check to your code. authenticate_user! authenticates that the person making the request is logged in. However, it does not check if the user is authorized to make the request they're making.
See Authentication versus Authorization for a bit more discussion on the two concepts. And take a look at https://stackoverflow.com/a/25654844/868533 for a good overview of popular authorization gems in Rails. To be clear, you almost definitely want a way to authenticate users (Devise) along with an authorization gem.
Assuming you decide to go with CanCanCan (which is a common option that I've used in past), you'd add an Ability class like:
class Ability
include CanCan::Ability
def initialize(user)
if user.present?
can :destroy, Post, user_id: user.id
end
end
end
Then you could add before_action :check_authorization, only: %i[destroy] as a new before_action on your controller and your tests should pass without any modification.
Remember. you are writing a controller test. So this test is unit test. There are two main ways to authorize in Devise. They are authorize routes and authorize controller. If you using authorize routes, when you write rspec for controller, you must use stub to counterfeit an authorize access.
Thru the Shopify API I can create webhooks great. I've created ones that point to RequestBin just fine, the data that is sent to RequestBin is perfect. But when I turn it towards my own controllers nothing happens. I have a binding.pry in my controller to catch anything that comes in, and nothing ever comes in. Not sure why??
Here is me creating the hook with the ShopifyAPI gem:
webhook = ShopifyAPI::Webhook.create(:topic => "carts/update",
:format => "json",:address => "https://dyno-shipping-trimakas.c9users.io
/webhook/cart_callback/")
And then here it is verified when I list the webhooks:
{"id"=>226014599,
"address"=>
"https://dyno-shipping-trimakas.c9users.io/webhook/cart_callback/",
"topic"=>"carts/create",
"created_at"=>"2016-02-24T17:20:02-05:00",
"updated_at"=>"2016-02-24T17:20:02-05:00",
"format"=>"json",
"fields"=>[],
"metafield_namespaces"=>[]}
This is my controller:
class WebHookController < ApplicationController
skip_before_action :verify_authenticity_token, only: [:cart_callback]
def cart_callback
binding.pry
x = params
pp x
end
end
Then finally this is my route:
scope '/webhook', :controller => :webhook do
post :cart_callback
end
Not sure where I went astray??
I'm trying to not require authentication for my reports view action. However when I try to use the devise method skip_before_action or skip_before_filter, it will still revert me back to the login page.
Here is my application controller.
class ApplicationController < ActionController::Base
acts_as_token_authentication_handler_for User
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :require_user
layout :check_layout
protected
def check_layout
devise_controller? ? 'legacy' : nil
end
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email) }
end
private
def require_user
authenticate_user!
end
end
Here is my reports controller
class ReportsController < ApplicationController
skip_before_action :require_user, only: [:view]
skip_before_action :verify_authenticity_token, only: [:transition]
before_action :check_params, only: [:transition]
before_action :set_report, only: [:show, :edit, :update, :destroy, :draft, :copy_edit, :review_and_approve, :revise, :approve, :archive, :mail_merge, :draft_from_queue]
end
How do I do I go about not requiring authentication for the report view action?
Your filters look ok, but the problem is that you are not hitting the view action. You are hitting the index action:
Started GET "/reports" for 10.0.2.2 at 2015-07-31 23:48:55 +0000
...
Processing by ReportsController#index as HTML
Completed 401 Unauthorized in 29ms
I have this AdminController
class Admin::AdminController < ApplicationController
before_filter :is_admin?
def dashboard
end
def is_admin?
redirect_to root_path, :flash => { :alert => "You are not an admin!" } if !current_user.admin?
end
end
and this other controller that inherits from the above:
class Admin::CompetitionEntriesController < Admin::AdminController
before_action :set_competition_entry, only: [:show, :edit, :update, :destroy]
....
end
My route file is:
Foo::Application.routes.draw do
root 'competition_entries#index'
devise_for :users
resources :competition_entries
namespace :admin do
root 'admin#dashboard'
resources :competition_entries
end
....
..
.
end
Now why am I getting this error when I am trying to reach 'http://localhost:3000/admin'
Missing template admin/admin/dashboard...
I am getting this extra admin? Why? I don't want to use scopes I want to use namespaces.
Thanks.
Routes do not affect default search paths for templates. If your controller class is named Foo::BarController, Rails will look for the templates in app/views/foo/bar/ unless you specify otherwise.
I'd like to verify if the user is logged in on every single request to the server.
Something like:
:before_filter verify_logged_in
Where should I put that before_filter so it applies to all controller actions and all requests?
To ensure that filters apply to all actions, place it in the application_controller.rb.
Application Controller is the base class of all other classes.
If you put any filter in this class then the flow works as follows:
If you hit url say of users resource with any action say index action then:
The control first goes to Application Controller. There it checks for filters, if finds any then it executes the filter method and after that it goes to index action of users controller.
Application Controller:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :verify_logged_in
end
Other Controller:
class UsersController < ApplicationController
def index
end
Here in the above code you see that the other controller is inheriting the contents of parent controller which is application controller. So if you put before_filter in the application controller then for every user it will verify if the user is logged in for each request.
put before_filter in the base class(in application_controller.rb file), it will work on base and all its derived classes, such as
class ApplicationController < ActionController::Base
before_filter :set_locale
def set_locale
I18n.locale = params[:locale] or I18n.default_locale
end
end
good luck :-)
Place it in the ApplicationController and inherit all other controllers from it. If you don't overwrite verify_logged_in in one of your sub-controllers it simply works.