I have a controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
http_basic_authenticate_with name: "tencet", password: "qk35lm"
def index
#posts = Post.order(:updated_at).reverse_order
end
def show
end
def new
#post = Post.new
end
end
But I don't want authentication for page "new", how can I do it?
You should specify except option because this method using before_action inside:
http_basic_authenticate_with name: "tencet", password: "qk35lm", except: [ :new ]
Here is documentation for http_basic_authenticate_with helper.
Related
class MoviesController < ApplicationController
before_action :set_movie, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#movies = Movie.all
end
def current_user_movie_index
#movies = current_user.movies
end
end
I want to show all the movies for all user on index, but I want to add a new link to show only the movies for current_user. Will I do a new action? can I use the same index view for both?
Like this:
controller:
class MoviesController < ApplicationController
before_action :set_movie, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#movies = Movie.all
end
def current_user_movies
#movies = current_user.movies
end
end
routes:
resources :movies do
get :current_user_movies, on: :collection
end
It will now be available at /movies/current_user_movies.
I'm working on review applications. In this app, users can post their own project and other users review to it. But I faced this error when other users try to edit their own reviews. To edit a project is only allowed to project owners. But to edit reviews should be allowed to users who wrote its review.
How can I divide this authentication?
/controllers/projects_controller.rb
class ProjectsController < ApplicationController
before_action :signed_in_user, only: [:new, :create, :edit, :update, :destroy]
before_action :set_project, only: [:show, :edit, :update]
before_action :correct_user, only: [:edit, :update]
def edit
end
def update
#project.attributes = create_params
if #project.save
redirect_to #project
else
render edit_project_path(id: #project.id)
end
end
private
def signed_in_user
unless user_signed_in?
redirect_to root_path
end
end
def set_project
#project = Project.find_by_id(params[:id])
end
def correct_user
unless current_user.projects.include?(#user)
redirect_to root_path
end
end
end
/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
before_action :set_projectct, only: [:new, :create, :edit, :update]
before_action :set_review, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
def edit
end
def update
#review.attributes = create_params
if #reviews.save
redirect_to prokect_path(id: #review.project_id)
else
redirect_to project_path(id: #review.project_id)
end
end
def set_project
#project = Project.find_by_id(params[:project_id])
end
def set_review
#review = Review.find_by_id(params[:id])
end
def correct_user
unless current_user.review.include?(#review)
redirect_to root_path
end
end
end
routes.rb
resources :projects do
resources :reviews
end
If you want to verify that the user has written the review, why not compare like this:
#review.user == current_user
By the way, this is an authorization verification, so if the user does not have the right to edit the review, you should return a 403 (Forbidden) instead of a redirect.
Gems like pundit or cancancan may help you doing that properly
My codebase contains callbacks like
prepend_before_action :authenticate_api_user! and
before_action :authenticate_api_v1_user!
What is the difference between these two?
Generally before_action runs before every action to a method and
prepend_before_action does what it says. It just add your definition at index zero.
Here is a great use case to prove the same:
class ConfuseUsersController < ApplicationController
prepend_before_action :find_user, only: [:update]
prepend_before_action :new_user, only: [:create]
before_action :save_and_render
def update
end
def create
end
private
def find_user
#user = User.find(params[:id])
end
def new_user
#user = User.new
end
def save_and_render
persited = #user.persited?
#user.assign_attributes(user_params)
if #user.save
redirect users_path(#user)
else
render (persited ? :edit : :new)
end
end
end
before_action :save_and_render this makes save_and_render to get called before every action.
prepend_before_action :find_user, only: [:update] This prepends find_user function to get called before save_and_render
Another example:
We have an ApplicationController where...
class ApplicationController < ActionController::Base
before_action :one
before_action :three
end
Now in any controller if we want to execute any other method for e.g. two before three you can use prepend_before_action like
prepend_before_action :three, :two
class AdminsController < ApplicationController
prepend_before_action :three, :two
end
Now before three gets executed two will get execute and then three for this specific method.
I have next definitions:
ApplicationController:
before_action :set_resource, only: [:edit, :update, :destroy]
...
private
def set_resource
...
OtherController < ApplicationController
before_action :set_resource, only: [:new_action1, :new_action2]
...
def new_action1
....
def new_action2
I expect set_resource method will be called before actions edit, update, destroy, new_action1, new_action2, but it right only for methods: edit, update, destroy
I faced almost the same problem today while writing plugin for Redmine.
There is an issues_controller with callbacks in Redmine:
OtherController < ApplicationController
before_filter :find_issue, :only => [:show, :edit, :update]
before_filter :authorize, :except => [:index]
end
And I added another action in module that is being included in issues_controller:
module IssuesControllerPatch
def self.included(base)
before_filter :find_issue, :only => [:show, :edit, :update, :merge]
end
end
What happened here is newly added filter for merge action gets called after authorize method, thus failing authorization.
To solve the problem, I overrode authorize method like that:
module IssuesControllerPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method :default_authorize, :authorize
alias_method :authorize, :authorize_with_merge
end
end
module InstanceMethods
def authorize_with_merge
find_issue if params[:action] == "merge" && params[:controller] == "issues"
default_authorize
end
end
end
Not quite elegant, but works like a charm. This should help you, as well.
I'm creating a rails app that includes devise.
I'm trying to add Twilio messaging to my site with Ngrok, i used this tutorial:
https://www.twilio.com/blog/2016/04/receive-and-reply-to-sms-in-rails.html
I was able to open Ngrok in the console and get the web-id they give for my url.
I keep getting this error when I plug the url into my browser ..I'm supposed to get to my own rails local app. Not sure whats wrong.
What I added in my messaging controller made for ngrok:
class MessagesController < ApplicationController
skip_before_filter :verify_authenticity_token
skip_before_filter :authenticate_user!, :only => "reply"
def reply
message_body = params["Body"]
from_number = params["From"]
boot_twilio
sms = #client.messages.create(
from: Rails.application.secrets.twilio_number,
to: from_number,
body: "Hello there, thanks for texting me. Your number is #{from_number}."
)
#twilio expects a HTTP response to this request
end
private
def boot_twilio
account_sid = Rails.application.secrets.twilio_sid
auth_token = Rails.application.secrets.twilio_token
#client = Twilio::REST::Client.new account_sid, auth_token
end
end
really unsure what is wrong.
when its not connecting to the 'def reply' and authenticate_user should be defined by devise.
Twilio developer evangelist here.
It looks like this was a problem that Rails 5 seems to have introduced. If the filter hasn't been defined by the time it is used in a controller it will raise an error. This was discovered in the Clearance project too.
Their fix was to pass the raise: false option to skip_before_filter:
class MessagesController < ApplicationController
skip_before_filter :verify_authenticity_token
skip_before_filter :authenticate_user!, :only => "reply", :raise => false
end
I had a similar issue to this when I was working on a Rails 6 application with Devise gem for authentication and authorization.
I added a skip_before_action :authenticate_admin!, only: [:index, :show] to the Products controller
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
skip_before_action :authenticate_admin!, only: [:index, :show]
def index
#products = Product.all
end
.
.
.
end
And it was throwing the error below when I visit the Products page:
Before process_action callback :authenticate_admin! has not been defined
Here's how I fixed it:
To use the skip_before_action :authenticate_admin!, only: [:index, :show] in the Products controller, I first needed to define the before_action :authenticate_user! in the application_controller:
# app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_admin!
end
Now I can use the skip_before_action :authenticate_admin!, only: [:index, :show] in the Products controller:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
skip_before_action :authenticate_admin!, only: [:index, :show]
def index
#products = Product.all
end
.
.
.
end
An alternative, if I don't want to define the before_action :authenticate_user! in the application_controller is to use the before_action :authenticate_admin!, except: [:index, :show]:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
before_action :authenticate_admin!, except: [:index, :show]
def index
#products = Product.all
end
.
.
.
end
That's all.
I hope this helps