The following is a standard posts#create action (app/controllers/posts_controller.rb).
At the controller level, I want to prevent an anonymous user (a user who is not signed in) from being able to save a post. As a secondary objective, I don't even want to execute the Post.new line if the user is not signed in. I want to know what is the best practice for accomplishing this.
Also, as a side note, I am unsure of how to write the json portion of the response. If I am redirecting with an alert message in the HTML context, what would be a good thing to respond with in JSON world?
def create
#posting = Post.new(posting_params)
respond_to do |format|
if #posting.save
format.html { redirect_to #posting, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #posting }
else
format.html { render action: 'new' }
format.json { render json: #posting.errors, status: :unprocessable_entity }
end
end
end
For the time being I have the following line in my code, above the Post.new line:
redirect_to home_path, warning: 'You must be logged in to post.' and return unless user_signed_in?
I suppose another option is something like the following, placed above the if #posting.save line. But really, I am looking to see what other folks would do.
unless user_signed_in?
format.html { redirect_to home_path, alert: 'You must be logged in to post.' and return }
format.json { render json: .....not sure what to put here..... }
end
Your advice is greatly appreciated.
A before_filter is good for this sort of thing:
before_filter :confirm_user_signed_in, only: [:new, :create]
def confirm_user_signed_in
unless user_signed_in?
respond_to do |format|
format.html { redirect_to home_path, alert: 'You must be logged in to post.' and return }
format.json { render json: .....not sure what to put here..... }
end
end
end
As far as what to render in the JSON scenario, you can render nothing at all, but with a 403 (Forbidden) status. You can optionally include some data explaining why the 403 occurred, but there's no standard for how that data will be displayed. Some frameworks (Backbone, I think) will look for a hash containing an errors key, which can be set to the reason.
Something like:
format.json { render json: { errors: ["Login required."] }, status: 403 }
The better practice is to use before filter and mention list of actions like this:
before_filter :require_login, :only => [:new, :create]
Try using the cancan gem. You can not only prevent the unwanted user from posting, also you can do various other permissions, which do not bloat the controller. These permissions are defined by you in a separate file called ability.rb.
cancan Railscast
cancan Github
Related
the following is my code for one of my controllers I have written for my Rails show reviewing application. Note that I that I did not use Devise for user auth.
The problem I am facing right now is that I want the user (pco) to only be able to update the show if he/she is the one which originally uploaded it. Here, authorized_as_pco_to_show can determine that but it needs the #show to be passed into it as a parameter. Therefore, I cannot use before_action.
The way I have it right now is to put this authorized_as_pco_to_show method at the start of every action which only allows for the correct pco to access it. I was wondering if there would be a better way of doing this. Any help would be much appreciated!
def update
authorized_as_pco_to_show #show
respond_to do |format|
if #show.update(show_params)
format.html { redirect_to #show, notice: "Show was successfully updated." }
format.json { render :show, status: :ok, location: #show }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #show.errors, status: :unprocessable_entity }
end
end
end
You can pass parameters to before actions if required. Instead of this:
before_action :authorized_as_pco_to_show
You can use:
before_action do
authorized_as_pco_to_show #show
end
However, as mentioned in the comments, you'll need to get that show from somewhere. Assuming you have another before_action along the lines of load_show which loads it into an instance variable, you can then just use that within your other before_action. Something like this:
before_action :load_show, :authorized_as_pco_to_show
# your actions here
private
def load_show
#show = Show.find(params[:id])
end
def authorized_as_pco_to_show
#show.authorized? # replace with whatever your checks are
end
This seems simple, but I can't find anything on it.
I have a link to request a meeting with another user. This produces a url like this:
http://localhost:3000/meetings/new?requestee_id=5
The requestee id, and other information in the form are passed to the MeetingController:
def create
requestor = current_user
#meeting_with_params = meeting_params
#meeting = Meeting.new(meeting_params)
respond_to do |format|
if #meeting.save
format.html { redirect_to home_url, notice: 'Your lex was successfully requested! Click Plan Meeting ' }
format.json { render :show, status: :created, location: #meeting }
else
format.html { render :new, params: #meeting_with_params }
format.json { render json: #meeting.errors, status: :unprocessable_entity }
end
end
end
If there are errors, the url now looks like this:
http://localhost:3000/meetings
which means the form will never submit since the requestee_id is not present.
What is right way to have the user see errors, but the url params are never reset?
Thanks!
You can do this:
format.html { render "path/to/new?{#meeting_with_params.to_param}" }
OR
You can use rails path helpers. You canrake routes and find your url's helper path (it looks something like this: edit_user_path, new_user_path)
format.html { render new_meeting_path(#meeting_with_params) }
As you are ok to use ajax for this checkout [https://github.com/rails/jquery-ujs](jquery-ujs), your form tag has to look something like this:
form_for #meeting, data: {remote: true} ...
I'm new in Rails world, but this Double render error a little bit interesting. I have never met this problem in PHP or in ASP.NET.
So I have two actions in one controller which is extended via Devise's Register controller.
I want to achieve that If the user logged in the two new_with_school and create_with_school actions will be redirected by the redirect_signed_in_user function.
class RegistrationsController < Devise::RegistrationsController
def new_with_school
redirect_signed_in_user
build_resource({})
resource.build_school
respond_with self.resource
end
def create_with_school
redirect_signed_in_user
build_resource(sign_up_params_with_school)
resource.school = School.new(sign_up_params_with_school[:school_attributes])
resource.school.user = resource
resource.role = 1
resource.save
respond_to do |format|
if resource.save
format.html { redirect_to after_sign_up_path_for(resource) }
format.json { render :show, status: :created, location: resource }
else
clean_up_passwords resource
set_minimum_password_length
format.html { render :new_with_school }
format.json { render json: resource.errors, status: :unprocessable_entity }
end
end
end
protected
def redirect_signed_in_user
redirect_to '/' if user_signed_in?
end
end
I gave this error message:
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
So it seems in Rails I can use only one redirect function in action.
But in this case, how can I achieve that if the user logged in, those 2 actions cannot reachable by the logged in user. The most beautiful solution will be, it the webapp could somehow redirect users to the root path (without throw an exception), so I want the most user friendly solution.
How can I do this? (I have read the devise code, but I cannot figure out how the root redirection works in)
Use this:
def redirect_signed_in_user
redirect_to '/' && return if user_signed_in?
end
I want my page to refresh once a record has been created, at the moment it directs it to the page before. here is the code from my controller:
def create
#license = License.new(params[:license])
respond_to do |format|
if #license.save
format.html { redirect_to :controller => 'customers', :action => 'index' }
format.json { render json: #customer, status: :created, location: #customer }
else
format.html { render action: "new" }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
where it says redirect_to i need that to refresh, or link to the current page, with the current id, which would be :controller => 'customers', :action => 'show' but with the id of the current page's record.
Try
redirect_to customer_path(#license.id)
instead.
Depending on what your routes.rb file says, it should work.
But if it doesn't, try:
redirect_to show_customer_path(#license.id)
However, here I have to assume that somehow, your customers_controller.rb is somehow showing records from the License model. If License and Customer are separate models, you will have to find the customer_id in some other way.
Perhaps, it is:
redirect_to customer_path(#license.customer_id)
If License is not connected to Customer in any way, you will need to pass it in as part of the post request.
Try,
I think you are passing customer_id to on params(Licensee belongs to customer), if so then
redirect_to customer_path(#license.customer) or redirect_to customer_path(params[:customer_id])
I'm trying to curl POST my Rails application in order to create a new Entry object. The problem is my entries_controller Create action looks like this:
def create
#user = current_user
#entry = #user.entries.build(params[:entry])
respond_to do |format|
if #entry.save
format.html { redirect_to landing_page_url, notice: 'Entry was successfully created.' }
format.json { render json: #entry, status: :created, location: #entry }
else
format.html { render action: "new" }
format.json { render json: #entry.errors, status: :unprocessable_entity }
end
end
end
Calling #user.entries.build just returns an exception because current_user doesn't exist. The thing is the Create action works well when I use the browser to create an Entry (as I login and create the current_user variable) but I do not know if it's possible to curl POST and create an Entry without changing the controller logic. And if it's not possible, could someone help reach the right direction towards building the controller logic (compatible with curl POST)?
I'm sorry if this is a dumb question, I'm fairly new to all this.
PS: I'm using Rails 3.2.3, if that's of any help.
Not sure how you create current_user (what it's based off), but this must exist for your method to work.
If you can pass a param to your action that specifies the user and set current_user as current_user ||= params[:user]... beware of the security implications of this.
You really should have a before_filter on your action to set current_user (via login, I'm presuming).