Rails 3 - Error Validation on nested resource - ruby-on-rails

I'm really struggling with how to handle Error Handling in Rails 3. I have hacked some ideas that could work but would rather do it the proper way. If anyone can help or give guidance I would appreciate it. Here is what I have so far
ItemController
def show
#item = Item.find(params[:id])
#note = #item.notes.new
respond_with(#item)
end
NoteController
def create
#note = #item.notes.build(params[:note])
flash[:notice] = 'Your note was successfully added!' if #note.save
respond_with(#item)
end
items/show.html.erb
<%= form_for ([#item, #note]), :html => {:id => 'form-add-item-note'} do |f| %>
I have tried
<%=f.error_messages%>
<%=error_messages_for :note%>
<%=error_messages_for :item,:note%>
and even have a template for handling errors
<%= render "global/error_messages", :target => #item %>
which contains
<% if target.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(target.errors.count, "error") %> prohibited this record from being saved:</h2>
<ul>
<% target.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
I believe I'm losing the errors to the redirect but I can't seem to quite get how to redirect or render the item controller from the failed save in the note create and I would love to be able to pass the error global template the #note and it render the errors

If you redirect, you'll lose any error messages. You need to render a view, instead of redirecting if your object isn't valid and does not save. I'm not sure yet of the best way to do it in rails3 with the respond_with method, but if you look at the scaffolding rails3 generates, you see how handling failed saves works.
respond_to do |format|
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end

Related

Form hidden fields and security

I m using hidden field in my app to add user_id to my database "Camping". I have associations "User" has many campings and "Camping" belongs_to "user".
When I run firebug or something like this, I can modify user_id value of this field. If any user puts his ID, I can modify object to other user... I want to avoid this !
My code
<%= f.hidden_field :user_id, :value => current_user.id %>
This code, is necessary because I allow only user to edit / updated / create object if they have user_id == current_user.id.
How to fix this security problem ?
By the way, I m using devise.
Edit with full code
My _form.html.erb
<%= form_for(camping) do |f| %>
<% if camping.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(camping.errors.count, "error") %> prohibited this camping from being saved:</h2>
<ul>
<% camping.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="form-group">
<label for="name">Nom du camping</label>
<%= f.text_field :name, autofocus: true, class:"form-control", id:"name", :required => true%>
</div>
<div class="actions">
<%= f.submit "Enregistrer", class:"btn btn-success" %>
</div>
<% end %>
my controller
def new
#camping = Camping.new
#campings = Camping.all
end
def edit
end
def create
#camping = Camping.new(camping_params)
respond_to do |format|
if #camping.save
format.html { redirect_to #camping, notice: 'Camping was successfully created.' }
format.json { render :show, status: :created, location: #camping }
else
format.html { render :new }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
def update
#camping = Camping.find(params[:id])
respond_to do |format|
if #camping.update(camping_params)
format.html { redirect_to #camping, notice: 'Camping was successfully updated.' }
format.json { render :show, status: :ok, location: #camping }
else
format.html { render :edit }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
my edit.html.erb
<div class="containershow">
<h1>Editing Camping</h1>
<%= render 'form', camping: #camping %>
<%= link_to 'Show', #camping %> |
<%= link_to 'Back', campings_path %>
</div>
my new.html.erb
<h1>New Camping</h1>
<%= render 'form', camping: #camping %>
<%= link_to 'Back', campings_path %>
Edit solution ?
User can create and update his camping. I delete hidden_field
def create
# #camping = Camping.new(camping_params)
#camping = Camping.new((camping_params).merge(:user_id => current_user.id))
respond_to do |format|
if #camping.save
format.html { redirect_to #camping, notice: 'Camping was successfully created.' }
format.json { render :show, status: :created, location: #camping }
else
format.html { render :new }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
In Devise, the current user object is in current_user, available to your controllers. When saving the model, make sure to fill the user id field from that object, and not user input in the update action of your controller. Note that the edit action does not matter, that just renders the edit page, the actual update happens in update (if you follow the default conventions). Of course if you don't want users to even see other users' objects, you also need access control in other controller actions like edit as well, but that (implementing access control in a multi-tenant Rails application) is a different and much broader question.
More generally, be aware that anything that comes from a request can very easily be forged by a user. Always implement security server-side and do not trust user input!
Edit (seeing your code)
To prevent users updating others' Campings, you need to check in update after getting the #camping object (the second line) whether that's a camping object that your logged on user (current_user.id) is supposed to be able to edit.
The same way, if you want to prevent users from creating Campings for other users, you need to make sure in create that user_id will be set to the current user, something like #camping.user_id=current_user.id.
Similarly, if you want to prevent having a look at each other's Campings, you need to add checks to edit, show and pretty much all actions that return such objects.
There are gems like cancan and cancancan that may help with access control in Rails, they are worth a look!
Your Question is quite interesting but simple In the any HTML View Any one can change anything this will cause a security wise vulnerability as well.
To avoid these issues we need to authenticate it by two way You have to check the code by like It should be use by Controller not by view.
Suppose If you are creating any article of particular user
So To avoid it what you can do You can set the User ID in Session and make a Helper Method to find Current User always
So that you can find current user directly from controller and create article according to user
def Create
#article = current_user.articles.create(article_params)
end
This kind of Two way checking you can put up so that It will be safe.
To avoid the spend time on these work you can use gem directly like Devise

Does rails form_for use the same route when using a symbol or instance var?

This is just a general question about the helper "form_for". I'm working on a program out of a textbook which has a form file using <%= form_for(#product) do |f| %>. The form is shared by the new and edit template. However, I've seen a lot of tutorials using a symbol (:product) instead of the instance variable. So, I tried swapping them to see what would happen. As it happens it gives me a routing error when trying to submit a form:
No route matches [POST] "/products/new"
and
No route matches [POST] "/products/5/edit"
Here's the code:
<%= form_for(:product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and in the product_controller
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
I thought I had read that "form_for :product" would search for an instance variable of the same name and use the same route, product_path, but it seems I was mistaken. I've looked at other posts on this site, but they don't seem to mention the routes. Why I'm getting this error?
EDIT: I added the option "url: products_path" to the form_for :product line and it works now. I'm guessing the symbol doesn't know to use the routes in resources like #product does?
If you have a routing error, please check the file confg/routes.rb it must include resources :products. If not, add it and restart the server.
And yes at routes we use a symbol with the table name (plural of model) for define resources.
edited:
With form_for use an instance variable: #product that was initialized on controller with an empty new product, or the finded product to update.

Have error message show when form is created through AJAX

I have a page called /add that you can add a Dog on and the form is in its own partial. I'm using Simple Form and Twitter Bootstrap. I added the files for the main Bootstrap but use a gem for simple_form to work with it just so you know.
DogsController
# new.js.erb (deleted new.html.erb)
def new
#dog = Dog.new
respond_to do |format|
format.js
end
end
# create.js.erb
def create
#dog = current_user.dogs.new(params[:dog])
respond_to do |format|
if #dog.save
format.html { redirect_to add_url, notice: 'Dog was successfully added.' }
format.json { render json: #dog, status: :created, location: #dog}
format.js
else
format.html { render 'pages/add' }
format.json { render json: #dog.errors, status: :unprocessable_entity }
end
end
end
dogs/_form.html.erb
<%= simple_form_for(#dog, :remote => true) do |f| %>
<%= render :partial => "shared/error_message", :locals => { :f => f } %>
<%= f.input :name %>
<%= f.button :submit, 'Done' %>
<% end %>
This line: <%= render :partial => "shared/error_message", :locals => { :f => f } %>
Is for bootstrap so it renders the errors html correctly.
PagesController
def add
respond_to do |format|
format.html
end
end
pages/add.html.erb
<div id="generate-form">
</div>
dogs/new.js.erb
$("#generate-form").html("<%= escape_javascript(render(:partial => 'dogs/form', locals: { dog: #dog })) %>");
Now how would I get this to render the error partial as if it was still on my dogs/new.html.erb since its being created through AJAX? I don't need client side validations do I?
EDIT
shared/_error_message.html.erb
<% if f.error_notification %>
<div class="alert alert-error fade in">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= f.error_notification %>
</div>
<% end %>
Through our chat you mentioned you also had a create.js.erb and that file was clearing out the form.
making the the create.js the same as new.js.erb :
$("#generate-form").html("<%= escape_javascript(render(:partial => 'dogs/form', locals: { dog: #dog })) %>");
made it work.
You don't have to do client side validations. But should,it is common to disable the submit button via js until client side validation is met.
also I would not delete the new.html.erb incase a client doesn't.have js turned on.
I think your add may need format.js to your add and
remote = true to your shared errors partial call

Issue with flash messages in ruby 1.9.3 and rails 3.2.2

I have a issue with flash message in my application. Actually in my application i have used the devise for users authentication and my application with ruby 1.9.3 and rails 3.2.2.
When an user is login, logout and sign up for new account the devise flash[:notice] is working fine.
In Rails flash[:notice] and flash[:alert] are the default flash messages.
The flash messages are display only once when page reload or when the user negative from one page to other page
The issue is when user is login the devise flash[:notice] is displaying but when i reload the page the flash[:notice] is displaying, but in rails the flash[:notice] will display only once
Actually the issue is when i try to create a new post i have redirect to the show page and i have write helper method for flash message this method i have call from the application layout for displaying the flash messages.
In controller create method
def create
#asset = Asset.new(params[:asset])
#asset.user_id = current_user.id
respond_to do |format|
if #asset.save
format.html { redirect_to #asset, alert: 'Asset was successfully created.' }
format.json { render json: #asset, status: :created, location: #asset }
else
format.html { render action: "new" }
format.json { render json: #asset.errors, status: :unprocessable_entity }
end
end
end
The Helper method for displaying flash messages
FLASH_TYPES = [:error, :warning, :success, :message,:notice,:alert]
def display_flash(type = nil)
html = ""
if type.nil?
FLASH_TYPES.each { |name| html << display_flash(name) }
else
return flash[type].blank? ? "" : "<div class=\"#{type}\"><p>#{flash[type]}</p> </div>"
end
html.html_safe
end
i have call this method form the application layout
= display_flash
I have tried with the flash[:alert], flash[:error],flash[:message] but no message display on the view page and i have tried with gem called flash_message this also displays only the
flash[:notice]
Please help me to solution this issue
Hy i am using using this approach to show flash message.First i make partial
_flash.html.erb in shared.The code of this partial
<% [:alert, :notice, :error].select { |type| !flash[type].blank? }.each do |type| %>
<p>
<% if flash[:notice] %>
<div class="alert-message error">
<h2 style="color: #ffffff;">Notice:</h2> <br/>
<%= flash[type] %>
</div>
<% elsif flash[:error] %>
<div class="alert-message error">
<h2 style="color: #ffffff;">Errors</h2> <br/>
<% flash[:error].each_with_index do |error, index| %>
<%= index+1 %>. <%= error %> <br/>
<% end %>
</div>
<% end %>
</p>
<% end %>
and i call it in application layout like this
<div id="flash">
<%= render :partial => 'shared/flash', :object => flash %>
</div>
And in controller use notice,alert like this
flash[:notice] = 'Admin was successfully created.'
flash[:alert] = 'Admin was successfully created.'
But for showing errors i use array because it may be more than one.Like this
def create
#user = User.new(params[:user])
#user.is_activated = true
# #user.skip_confirmation!
if #user.save
role = Role.find_by_name("admin")
RoleUser.create!(:user => #user, :role => role)
redirect_to :controller => '/administrator', :action => 'new'
flash[:notice] = 'Admin was successfully created.'
else
flash[:error]=[]
#user.errors.full_messages.each do |error|
flash[:error] << error
end
render :action => "new"
end
end
add this line in application.js
setTimeout("$('#flash').html(' ');", 10000);
Use it and enjoy!!!!!!!!!!!!!!

Rails does not display error messages on a form in a custom method

I've created a custom method called checkout in my app. I create an order (which is done my adding products to my "cart"), assign it to my client, and then I head to my checkout screen where I confirm the items and enter their customer order number and complete the order (submit).
Everything works great except that it doesn't display error messages. I'm able to display a flash error notice (seen in complete_order method) when things go wrong but it doesn't specify the details like a normal form would. The error messages should appear if the customer order number is not unique for that client.
Below is the custom method (checkout) related code.
Order Model:
validates_uniqueness_of :customer_order_number, :scope => :client_id
Orders_controller:
def checkout
#order = current_order
end
def complete_order
#order = current_order
respond_to do |format|
if #order.update_attributes(params[:order])
#order.complete #sets submitted datetime and state to 'complete'
flash[:notice] = 'Thank you! Your order is being processed.'
format.html { redirect_to( products_path ) }
format.xml { head :ok }
else
flash[:error] = 'Please review your items' #added to confirm an error is present
format.html { redirect_to( checkout_path ) }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
end
And the form in the checkout view:
<% form_for #order, :url => { :controller => "orders", :action => "complete_order" } do |f| %>
<%= f.error_messages %>
<%= f.text_field :customer_order_number, :label => "Purchase Order Number" %>
<p>
<%= f.submit 'Complete Order', :confirm => 'Are you sure?' %> <small> or <%= link_to 'cancel', current_cart_path %></small>
</p>
<% end %>
Any idea how I can display the specific error messages?
Change redirect_to to render in else condition otherwise checkout method get called again & no error will displayed.
else
format.html { render :action => 'checkout' }

Resources