Ruby on Rails
Thursday, July 30, 2015
Imagine we had a basic survey application (Rails 4.2 with Devise) similar to the following:
-- class User < ActiveRecord::Base
# Assume devise user here
has_many :user_surveys
has_many :surveys, through: :user_surveys
has_many :responses
end
class UserSurvey < ActiveRecord::Base
belongs_to :user
belongs_to :survey
end
class Survey < ActiveRecord::Base
has_many :questions
has_many :user_surveys
has_many :users, through: :user_surveys
accepts_nested_attributes_for :questions
# name: string
end
class Question < ActiveRecord::Base
belongs_to :survey
has_many :responses
accepts_nested_attributes_for :responses
# response_text: string
end
Class Response < ActiveRecord::Base
belongs_to :question
belongs_to :user
# response_text: string
end
The idea being, an admin can create a survey with questions, and then assign that survey to users through the join model UserSurvey.
Now imagine a basic form that we give the user for their survey that looks like this:
<h1><%= @survey.name %> Answers</h1>
<%= form_for(@survey) do |f| %>
<h3><%= current_user.name %></h3>
<table>
<thead>
<tr>
<td>Questions</td>
<td>Response</td>
</tr>
</thead>
<tbody>
<% @questions.each do |question| -%>
<tr>
<td><%= question.question_text %></td>
<td>
<%= f.fields_for :questions, question do |q| -%>
<%= q.fields_for :responses, question.responses.find_or_initialize_by(user: current_user.id) do |r| -%>
<%= r.text_area :response_text %>
<%= r.hidden_field :user_id, current_user.id %>
<% end -%>
<% end -%>
</td>
</tr>
<% end -%>
</tbody>
</table>
<div class="actions">
<%= f.submit %>
</div>
<% end -%>
My main question revolves around the following line (under fields_for responses):
<%= r.hidden_field :user_id, current_user.id %>
I see it suggested other places (eg: here on stackoverflow, or this tutorial here ) to put a hidden user_id field, but this feels incredibly wrong to me -- if a user is malicious, they could edit the form and modify the hidden user_id field to modify another participants answer.
Now, the one solution I did think was that on the controller side I could mess with the params, IE assume the submitted parameters look as follows
"survey"=>{"questions_attributes"=>{"0"=>{"responses_attributes"=>{"0"=>{"response_text"=>"one"}}, "id"=>"7"}, "1"=>{"responses_attributes"=>{"0"=>{"response_text"=>"two"}}, "id"=>"8"}}}
I could do something like:
def update
@survey = current_user.surveys.find(params[:id])
@questions = @survey.questions
params[:survey][:question_attributes].each do |key, attribs|
question = @questions.find(attribs[:id])
response = question.responses.where(:user_id => current_user.id).first_or_initialize
response.response_text = attribs["responses_attributes"]["0"]["response_text"]
response.save!
end
end
This way, I not only check to make sure the survey is attached to the user, but also make sure the questions they are submitting are attached to that specific survey, and the responses are not only tied to the correct question, but also back to the correct user. This feels messy though, and I feel I'm majorly overthinking this.
Can anyone give me a sanity check as to what I'm doing wrong?
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-talk+unsubscribe@googlegroups.com.
To post to this group, send email to rubyonrails-talk@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/42e4e4d4-9530-4c08-b397-29b5efb1d30a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment