How does rails params are created? - ruby-on-rails

I'm having a trouble when I'm trying to user params.require(...).permit(...)
In my application I received the follow param dic:
{"utf8"=>"✓",
"authenticity_token"=>"Vatzcb5tgTu2+wL1t6Of+FbIK8Ibp+tM03Naai4b2OU=",
"/login"=>{"username_or_email"=>"jonatasteixeira",
"password"=>"[FILTERED]"},
"commit"=>"Save /login" }
I would like to know why the my key received the "/login" name.
My view:
<h1>Login</h1>
<%= form_for(login_path) do |f| %>
<div class="field">
<%= f.label :username_or_email %><br>
<%= f.text_field :username_or_email %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.password_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', root_path %>
In my controller
class SessionsController < ApplicationController
# GET /login
def new
#user = User.new
end
# POST /login
def create
#user = User.find_by_emai(session_params[:username_or_email]) || User.find_by_username(session_params[:username_or_email])
if #user && #user.authenticate(session_params[:password])
session[:current_user_id] = #user.id
flash[:notice] = 'You are logged in'
else
flash[:notice] = 'Invalid password, username or email'
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def session_params
logger.info :login
params.require("/login").permit(:username_or_email, :password)
end
end
I dont want to use "/login" as key, I would like to use :login. Some one knows how could I adjust it?
Thanks!!

As #Rafal pointed out, you could code your call to form_for like this to get rid of the awkward /login key in your params:
<%= form_for(:login) do |f| %>
Strong parameters are really only for scenarios where you are doing mass assignment on an object. If you were creating the user, for example, then you would probably want to pass the attributes into the new initializer method using strong parameters.
#user = User.new(session_params)
But because you're not doing mass assignment in this case, you can just pass in the values directly without a session_params method:
# POST /login
def create
#user = User.find_by(email: params[:login][:username_or_email]) || User.find_by(username: params[:login][:username_or_email])
if #user && #user.authenticate(params[:login][:password])
session[:current_user_id] = #user.id
flash[:notice] = 'You are logged in'
else
flash[:notice] = 'Invalid password, username or email'
end
end
The whole point of strong parameters is so no one can pass in extra attributes. In your /login scenario, your code is completely in control of the values being handled, so you don't need to worry about it.

Form_For
When you use form_for, Rails expects an object to be passed so it can build a variety of different elements from it:
[The form_for] helper is designed to make working with resources much easier
compared to using vanilla HTML.
The problem is you're passing a route to this method, which I'm surprised actually works.
--
form_tag
You'll be better using a symbol, as recommended by the accepted answer, or by using form_tag, which doesn't require an object:
<%= form_tag login_path do %>
<%= text_field_tag :username_or_email %>
<%= password_field_tag :password %>
<%= submit_button_tag "Go" %>
<% end %>
This will remove the references to the "login" key of your params, and will give you the ability to do this (no need for require):
params.permit(:username_or_email, :password)

Instead of
<%= form_for(login_path) do |f| %>
use
<%= form_for(:login) do |f| %>

Related

How does hidden_field_tag works in an reset password form

It's being really hard to understand how a form work with hidden_field_tag in a specific situation. I wish someone would explain me what happens.
It's the form from reset password from railstutorial.
My view is:
<% provide(:title, 'Reset password') %>
<h1>Reset password</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user, url: password_reset_path(params[:id])) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= hidden_field_tag :email, #user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Update password", class: "btn btn-primary" %>
<% end %>
</div>
</div>
My update action and the strong params method:
def update
if params[:user][:password].empty?
#user.errors.add(:password, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params)
log_in #user
#user.update_attribute(:reset_digest, nil)
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
When access the edit action, the user enter an email an the app send a link to this email, with an activation token, like this path:
/password_resets/9Ij91DFChTeWTitNDVJfYw/edit?email=example%40railstutorial.org
The user open this link and change the password. That is perfectly functional. My question is: if I already have the user from the edit action and doesn't use params[:email] (provided with the hidden_field_tag) explicitly in my update action, why do I need the hidden tag?
Actions are separate from each other. When you first render your password reset form in scope of the edit action, your #user instance variable, being somehow initialized before, is used to supply a value for the hidden email field.
When later user submits the form, its data gets processed by the update action in a completely separate request. This request does not know what happened before, specifically, that the form was originally rendered by the edit action. It also doesn't have access to any of the objects that were there in scope of the edit action.
In order to process the request, your update action code needs to initialize all the objects it needs anew, and that is primarily the #user object.
If current action doesn't know what happened before, how it would know which user record should go to the #user variable? It can find the user by email, this is why you supplied it in the params. Your edit action code made it available for the next request.
From the look of your edit action, there should already be some method in your controller which initializes the #user object before the action code gets executed. Look for before_action callback.
The method probably looks up the user by the email passed in the params. When action itself gets executes, the #user object is already there so it might seem Rails somehow knows how to get it. It doesn't, your controller code makes this happen.
Therefore you need hidden tag to pass context between actions. There are other ways to do that, like using session or cookies, but this way is probably the simplest.

POST is going to 'new' and not 'create' Rails

So I am trying to implement the password_reset functionality into my site using bcrypt. An issue I am having is the POST is going to my new action rather to my create action.
My View
<%= form_for password_resets_path, method: 'post' do %>
<div>
<h3>Please enter your email address</h3>
<%= text_field_tag :email, params[:email] %>
</div>
<div>
<%= submit_tag "Reset Password" %>
</div>
My Controller
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => 'Email sent with password reset instructions.'
end
end
My Routes
resources :password_resets
And I am getting this error
ActionController::RoutingError (No route matches [POST] "/password_resets/new"):
I looked at different solutions already, and since I do not have a model the #object, would not work for me. Since I am simply just trying to call to an action.
I feel like I am missing something so very simple but for the life of me I have been unable to figure it out. Many thanks in advance to whomever is the one to help me.
Problem: <%= form_for password_resets_path, method: 'post' do %>
form_for needs an object. If you don't want an object, just use the form_tag helper:
<%= form_tag password_resets_path do %>
<%= text_field_tag :email, params[:email], placeholder: "Please enter your email address" %>
<%= submit_tag "Reset Password" %>
<% end %>
This should work for you.

Select_tag login

I would like to have a drop down menu with a list of all the user names in the db. From there, I would like the user to choose his/her name and be able to click login and be taken to their respective page. At this point, a password is not needed. Currently, I have the following:
controller:
def login
#user = User.new
#users = User.all
# #user = User.find_by_id(:id)
# redirect_to user_path(#user)
end
view:
<%= form_for #user, url: '/login', html: {method: 'get'} do |f| %>
<%= f.label "Name" %>
<br/>
<%= select_tag :user, options_for_select(#users) do |users| %>
<%= link_to users.name, users %>
<% end %>
<br/>
<br/>
<%= f.submit 'Login' %>
<% end %>
I cannot seem to link the user to their path and also, i want to show the users name in the drop down menu. Currently, it shows a hexidecimal pointer.
Thank you in advance.
You shouldn't be making a new User object here: you just want to load one out of the database. What you want to do in the controller is just to set current_user to be one of the existing users, right?
Also you've got the form submitting back to the action which loads the form in, which seems weird. I would make it submit to a new action, like "set_current_user" which is a POST action.
in your login template:
<%= form_tag '/set_current_user' do %>
<%= f.label "Name" %>
<br/>
<%= select_tag "user_id", options_for_select(#users.collect{|user| [user.name, user.id] } %>
<br/>
<br/>
<%= submit_tag 'Login' %>
<% end %>
in the controller (you'll need to amend routes.rb to make the '/set_current_user' go to this action) you then need to set something which will keep the user logged in. The traditional way to do this is via session[:user_id], and to have a method current_user which uses this.
def set_current_user
session[:user_id] = params[:user_id]
redirect_to "/" and return
end
Your initial approach is reminiscent of how this sort of thing is normally handled, wherein you do have a form_for, but it's for a UserSession object rather than a User object.

Rails: form_for security hole?

I have a user model with :name and :is_admin attributes. You should not change is_admin value. If you write a form in which any user may edit their name:
<%= form_for #user %>
<%= f.label :given_name %>
<%= f.text_field :given_name %>
<%= f.submit "Update" %>
<% end %>
Are you opening up a security hole?
Kind regards,
No.
This is because parameters are protected when they come into the controller by the strong parameters feature within Rails. In controllers now you define a create action like this:
def create
#user = User.new(user_params)
...
end
That user_params method looks like this:
def user_params
params.require(:user).permit(:name)
end
This code will permit the name parameter from the user parameters and outright reject everything else.
This is talked about in this section of the Getting Started guide.

The action 'create' could not be found for UserVacationDaysController

I've seen this question asked everywhere, but it never solves my problem. Heres my controller:
class UserVacationDaysController < ApplicationController
def new
#user = current_user
#user_vacation_days = UserVacationDay.new
end
def create
#user_vacation_days = UserVacationDay.create(params[:user_vacation_day])
#user_vacation_days.user = current_user
# #user_vacation_days.calculate_work_days
# (another param that holds date range will get passed in)
# puts #user_vacation_days.errors.inspect
if #user_vacation_days.persisted?
flash[:notice] = "Request Sent"
redirect_to dashboard_index_path
request_vacation_days # method from model. model method calls method in employee_mailer
else
flash[:notice] = "Something went wrong, please try again"
render :new
end
end
end
And here is my view (form).
<h2>Request Days Off</h2>
<%= form_for :user_vacation_days, :url => user_vacation_days_path do |f| %>
<div><%= f.label "How much time off would you like to take?" %>
<%= f.number_field :number_of_days %></div>
<div><%= f.label "Argue your case, slave" %>
<%= f.text_area :description %></div>
<div><%= f.submit "Request Time Off" %></div>
<% end %>
The routes for my 2 controller methods are
user_vacation_days POST /user_vacation_days(.:format) user_vacation_days#create
new_user_vacation_day GET /user_vacation_days/new(.:format) user_vacation_days#new
Does anyone have any idea what's going on? I've looked all over the place, and I can't find anything. I can't think of any reason why the controller method wouldn't be found. Thanks!
Instead of <%= form_for :user_vacation_days, :url => user_vacation_days_path do |f| %> what happens if you use <%= form_for #user_vacation_days, :url => user_vacation_days_path do |f| %>
Also, does a User have_many VacationDay? You might want to change to resourceful routes, and have vacation days nested.
config/routes.rb
resources :users do
resources :user_vacation_days
end
in your new action under UserVacationDaysContoller #may want to rename this to just VacationDays since the nesting implies
def new
#user = current_user
#user_vacation_days = #user.vacation_days.build
end

Resources